From 61c65cf0f97cb80c7438a98180303faf83685fd3 Mon Sep 17 00:00:00 2001 From: disrupted Date: Tue, 10 Feb 2026 19:18:34 +0100 Subject: [PATCH 1/3] style: format snapshots --- tests/data/ansi-codes.expected.json | 6516 ++++- tests/data/api-abort.expected.json | 128 +- tests/data/api-error.expected.json | 155 +- tests/data/cursor_data.expected.json | 229 +- tests/data/cursor_data.json | 706 +- tests/data/diagnostics.expected.json | 8505 +++++- tests/data/diagnostics.json | 24214 +++++++++++++++- tests/data/diff.expected.json | 342 +- tests/data/markdown-codefence.expected.json | 591 +- tests/data/mentions-with-ranges.expected.json | 385 +- tests/data/message-removal.expected.json | 223 +- .../multiple-messages-synthetic.expected.json | 278 +- tests/data/multiple-messages-synthetic.json | 12 +- tests/data/multiple-messages.expected.json | 313 +- tests/data/multiple-messages.json | 577 +- ...tiple-question-ask-reply-all.expected.json | 391 +- .../data/multiple-question-ask-reply-all.json | 990 +- .../data/multiple-question-ask.expected.json | 327 +- tests/data/multiple-question-ask.json | 338 +- tests/data/multiple-stacked-permissions.json | 2 +- tests/data/perf.expected.json | 430 +- .../permission-ask-new-approve.expected.json | 605 +- tests/data/permission-ask-new-approve.json | 794 +- .../permission-ask-new-deny.expected.json | 253 +- tests/data/permission-ask-new-deny.json | 392 +- tests/data/permission-ask-new.expected.json | 454 +- tests/data/permission-ask-new.json | 283 +- tests/data/permission-prompt.expected.json | 320 +- tests/data/permission.expected.json | 225 +- tests/data/permission_error.json | 1678 +- tests/data/planning.expected.json | 373 +- tests/data/question-ask-replied.expected.json | 231 +- tests/data/question-ask-replied.json | 685 +- tests/data/question-ask.expected.json | 327 +- tests/data/question-ask.json | 307 +- tests/data/reasoning.expected.json | 291 +- tests/data/redo-all.expected.json | 1061 +- tests/data/redo-all.json | 4279 ++- tests/data/redo-once.expected.json | 749 +- tests/data/redo-once.json | 4266 ++- tests/data/revert.expected.json | 542 +- tests/data/selection.expected.json | 290 +- .../shifting-and-multiple-perms.expected.json | 799 +- tests/data/simple-session.expected.json | 158 +- tests/data/tool-invalid.expected.json | 132 +- tests/data/updating-text.expected.json | 175 +- 46 files changed, 65270 insertions(+), 51 deletions(-) diff --git a/tests/data/ansi-codes.expected.json b/tests/data/ansi-codes.expected.json index 5e0ced07..b0d97def 100644 --- a/tests/data/ansi-codes.expected.json +++ b/tests/data/ansi-codes.expected.json @@ -1 +1,6515 @@ -{"lines":["----","","","** run** `Run all tests to verify refactoring`","","`````bash","> cd /Users/cam/Dev/neovim-dev/opencode.nvim && ./run_tests.sh","","Running tests for opencode.nvim","------------------------------------------------","Starting...Scheduling: ./tests/minimal/plugin_spec.lua","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/minimal/plugin_spec.lua\t","Success\t||\topencode.nvim plugin loads the plugin without errors\t","Success\t||\topencode.nvim plugin can be set up with custom config\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","✓ Minimal tests passed","------------------------------------------------","Starting...Scheduling: ./tests/unit/timer_spec.lua","Scheduling: ./tests/unit/server_job_spec.lua","Scheduling: ./tests/unit/config_spec.lua","Scheduling: ./tests/unit/api_spec.lua","Scheduling: ./tests/unit/event_manager_spec.lua","Scheduling: ./tests/unit/init_spec.lua","Scheduling: ./tests/unit/state_spec.lua","Scheduling: ./tests/unit/id_spec.lua","Scheduling: ./tests/unit/api_client_spec.lua","Scheduling: ./tests/unit/context_spec.lua","Scheduling: ./tests/unit/session_spec.lua","Scheduling: ./tests/unit/config_file_spec.lua","Scheduling: ./tests/unit/renderer_spec.lua","Scheduling: ./tests/unit/opencode_server_spec.lua","Scheduling: ./tests/unit/core_spec.lua","Scheduling: ./tests/unit/render_state_spec.lua","Scheduling: ./tests/unit/snapshot_spec.lua","Scheduling: ./tests/unit/keymap_spec.lua","Scheduling: ./tests/unit/util_spec.lua","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/server_job_spec.lua\t","Success\t||\tserver_job exposes expected public functions\t","Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t","Success\t||\tserver_job call_api rejects on non 2xx\t","Success\t||\tserver_job stream_api forwards chunks\t","Success\t||\tserver_job ensure_server spawns a new opencode server only once\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/util_spec.lua\t","Success\t||\tutil.parse_dot_args parses flat booleans\t","Success\t||\tutil.parse_dot_args parses nested dot notation\t","Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t","Success\t||\tutil.parse_dot_args parses numbers\t","Success\t||\tutil.parse_dot_args handles empty string\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_client_spec.lua\t","Success\t||\tapi_client should create a new client instance\t","Success\t||\tapi_client should remove trailing slash from base_url\t","Success\t||\tapi_client should create client using create factory function\t","Success\t||\tapi_client should have all expected API methods\t","Success\t||\tapi_client should construct URLs correctly with query parameters\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/session_spec.lua\t","Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t","Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t","Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t","Success\t||\topencode.session get_last_workspace_session handles empty session list\t","Success\t||\topencode.session get_by_name returns the session with matching ID\t","Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t","Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t","Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t","Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t","Success\t||\topencode.session read_json_dir skips invalid JSON files\t","Success\t||\topencode.session get_messages returns nil when session is nil\t","Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t","Success\t||\topencode.session get_messages returns messages with their parts\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_file_spec.lua\t","Success\t||\tconfig_file.setup lazily loads config when accessed\t","Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t","Success\t||\tconfig_file.setup get_opencode_project returns project\t","\t","Success: \t3\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_spec.lua\t","Success\t||\topencode.api commands table contains the expected commands with proper structure\t","Success\t||\topencode.api setup registers all commands\t","Success\t||\topencode.api setup sets up command functions that call the correct core functions\t","Success\t||\topencode.api Lua API provides callable functions that match commands\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/id_spec.lua\t","Success\t||\tID module should generate ascending session IDs\t","Success\t||\tID module should generate descending message IDs\t","Success\t||\tID module should validate given IDs correctly\t","Success\t||\tID module should throw error for invalid given IDs\t","Success\t||\tID module should validate schemas correctly\t","Success\t||\tID module should return available prefixes\t","Success\t||\tID module should generate IDs with correct length structure\t","\t","Success: \t7\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/render_state_spec.lua\t","Success\t||\tRenderState new and reset creates a new instance\t","Success\t||\tRenderState new and reset resets to empty state\t","Success\t||\tRenderState set_message sets a new message\t","Success\t||\tRenderState set_message updates line index for message\t","Success\t||\tRenderState set_message updates existing message\t","Success\t||\tRenderState set_part sets a new part\t","Success\t||\tRenderState set_part updates line index for part\t","Success\t||\tRenderState set_part initializes actions array\t","Success\t||\tRenderState get_part_at_line returns part at line\t","Success\t||\tRenderState get_part_at_line returns nil for line without part\t","Success\t||\tRenderState get_message_at_line returns message at line\t","Success\t||\tRenderState get_message_at_line returns nil for line without message\t","Success\t||\tRenderState get_part_by_call_id finds part by call ID\t","Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t","Success\t||\tRenderState actions adds actions to part\t","Success\t||\tRenderState actions adds actions with offset\t","Success\t||\tRenderState actions clears actions for part\t","Success\t||\tRenderState actions gets actions at line\t","Success\t||\tRenderState actions gets all actions from all parts\t","Success\t||\tRenderState update_part_lines updates part line positions\t","Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t","Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t","Success\t||\tRenderState update_part_lines returns false for non-existent part\t","Success\t||\tRenderState remove_part removes part and shifts subsequent content\t","Success\t||\tRenderState remove_part clears line index for removed part\t","Success\t||\tRenderState remove_part returns false for non-existent part\t","Success\t||\tRenderState remove_message removes message and shifts subsequent content\t","Success\t||\tRenderState remove_message clears line index for removed message\t","Success\t||\tRenderState remove_message returns false for non-existent message\t","Success\t||\tRenderState remove_message removes unrendered message without shifting\t","Success\t||\tRenderState shift_all does nothing when delta is 0\t","Success\t||\tRenderState shift_all shifts content at or after from_line\t","Success\t||\tRenderState shift_all shifts actions with parts\t","Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t","Success\t||\tRenderState shift_all invalidates index when content shifted\t","Success\t||\tRenderState shift_all exits early when content found before from_line\t","Success\t||\tRenderState update_part_data updates part reference\t","Success\t||\tRenderState update_part_data does nothing for non-existent part\t","Success\t||\tRenderState get_unrendered_message_ids returns empty list when no unrendered messages\t","Success\t||\tRenderState get_unrendered_message_ids returns list of unrendered message IDs\t","Success\t||\tRenderState get_unrendered_message_ids returns sorted list of message IDs\t","\t","Success: \t41\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/init_spec.lua\t","Success\t||\topencode has setup function in the public API\t","Success\t||\topencode main module can be required without errors\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/snapshot_spec.lua\t","Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t","Success\t||\tsnapshot.restore notifies error if no active session\t","Success\t||\tsnapshot.restore notifies error if read-tree fails\t","Success\t||\tsnapshot.restore notifies error if checkout-index fails\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/event_manager_spec.lua\t","Success\t||\tEventManager should create a new instance\t","Success\t||\tEventManager should subscribe and emit events\t","Success\t||\tEventManager should handle multiple subscribers\t","Success\t||\tEventManager should unsubscribe correctly\t","Success\t||\tEventManager should track subscriber count\t","Success\t||\tEventManager should list event names\t","Success\t||\tEventManager should handle starting and stopping\t","Success\t||\tEventManager should not start multiple times\t","\t","Success: \t8\t","Failed : \t0\t","========================================\t","Error detected while processing command line:","opencode command not found - please install and configure opencode before using this plugin","Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/core_spec.lua\t","Success\t||\topencode.core open creates windows if they don't exist\t","Success\t||\topencode.core open handles new session properly\t","Success\t||\topencode.core open focuses the appropriate window\t","Success\t||\topencode.core select_session filters sessions by description and parentID\t","Success\t||\topencode.core send_message sends a message via api_client\t","Success\t||\topencode.core send_message creates new session when none active\t","Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t","Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t","Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t","Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t","\t","Success: \t10\t","Failed : \t0\t","========================================\t","File not added to context. Could not read.","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/context_spec.lua\t","Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t","Success\t||\textract_from_opencode_message returns nils if message or parts missing\t","Success\t||\textract_from_message_legacy extracts legacy tags from text\t","Success\t||\textract_legacy_tag extracts content between tags\t","Success\t||\textract_legacy_tag returns nil if tag not found\t","Success\t||\tformat_message returns a parts array with prompt as first part\t","Success\t||\tformat_message includes mentioned_files and subagents\t","Success\t||\tdelta_context removes current_file if unchanged\t","Success\t||\tdelta_context removes mentioned_subagents if unchanged\t","Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t","Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t","Success\t||\tadd_file/add_selection/add_subagent adds a selection\t","Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/opencode_server_spec.lua\t","Success\t||\topencode.opencode_server creates a new server object\t","Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t","Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t","Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t","Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_spec.lua\t","Success\t||\topencode.config uses default values when no options are provided\t","Success\t||\topencode.config merges user options with defaults\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/keymap_spec.lua\t","Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t","Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t","Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t","Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t","Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t","Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t","Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t","Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t","Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t","Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t","Success\t||\topencode.keymap setup_permisson_keymap sets up permission keymaps when there is a current permission\t","Success\t||\topencode.keymap setup_permisson_keymap should delete existing permission keymaps if no current permission exists after being set\t","Success\t||\topencode.keymap setup_permisson_keymap does not set permission keymaps when there is no current permission\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/state_spec.lua\t","Success\t||\topencode.state (observable) notifies listeners on key change\t","Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t","Success\t||\topencode.state (observable) can unregister listeners\t","Success\t||\topencode.state (observable) does not notify if value is unchanged\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/timer_spec.lua\t","Success\t||\tTimer Timer.new creates a new timer with required options\t","Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t","Success\t||\tTimer Timer.new stores optional parameters\t","Success\t||\tTimer Timer:start starts a repeating timer\t","Success\t||\tTimer Timer:start starts a one-shot timer\t","Success\t||\tTimer Timer:start passes arguments to on_tick function\t","Success\t||\tTimer Timer:start stops timer when on_tick returns false\t","Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t","Success\t||\tTimer Timer:start stops previous timer before starting new one\t","Success\t||\tTimer Timer:start throws error when timer creation fails\t","Success\t||\tTimer Timer:stop stops a running timer\t","Success\t||\tTimer Timer:stop calls on_stop callback when provided\t","Success\t||\tTimer Timer:stop does nothing when timer is not running\t","Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t","Success\t||\tTimer Timer:is_running returns false when timer is not started\t","Success\t||\tTimer Timer:is_running returns true when timer is running\t","Success\t||\tTimer Timer:is_running returns false after timer is stopped\t","Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t","Success\t||\tTimer Integration tests can restart a stopped timer\t","Success\t||\tTimer Integration tests handles rapid start/stop cycles\t","\t","Success: \t20\t","Failed : \t0\t","========================================\t","Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc","Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO","","========================================\t","Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua\t","Success\t||\trenderer replays api-error correctly (event-by-event)\t","Success\t||\trenderer replays api-error correctly (session)\t","Success\t||\trenderer replays diff correctly (event-by-event)\t","Success\t||\trenderer replays diff correctly (session)\t","Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event)\t","Success\t||\trenderer replays mentions-with-ranges correctly (session)\t","Success\t||\trenderer replays message-removal correctly (event-by-event)\t","Success\t||\trenderer replays permission-denied correctly (event-by-event)\t","Success\t||\trenderer replays permission-denied correctly (session)\t","Success\t||\trenderer replays permission-prompt correctly (event-by-event)\t","Success\t||\trenderer replays permission correctly (event-by-event)\t","Success\t||\trenderer replays permission correctly (session)\t","Success\t||\trenderer replays planning correctly (event-by-event)\t","Success\t||\trenderer replays planning correctly (session)\t","Success\t||\trenderer replays redo-all correctly (event-by-event)\t","Success\t||\trenderer replays redo-all correctly (session)\t","Success\t||\trenderer replays redo-once correctly (event-by-event)\t","Success\t||\trenderer replays redo-once correctly (session)\t","Success\t||\trenderer replays revert correctly (event-by-event)\t","Success\t||\trenderer replays revert correctly (session)\t","Success\t||\trenderer replays selection correctly (event-by-event)\t","Success\t||\trenderer replays selection correctly (session)\t","Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event)\t","Success\t||\trenderer replays simple-session correctly (event-by-event)\t","Success\t||\trenderer replays simple-session correctly (session)\t","Success\t||\trenderer replays tool-invalid correctly (event-by-event)\t","Success\t||\trenderer replays tool-invalid correctly (session)\t","Success\t||\trenderer replays updating-text correctly (event-by-event)\t","Success\t||\trenderer replays updating-text correctly (session)\t","Success\t||\trenderer unrendered messages is_message_unrendered returns true for unrendered message\t","Success\t||\trenderer unrendered messages is_message_unrendered returns false for rendered message\t","Success\t||\trenderer unrendered messages is_message_unrendered returns false for nil\t","Fail\t||\trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t"," ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: Expected objects to not be the same."," Passed in:"," (nil)"," Did not expect:"," type nil"," "," stack traceback:"," \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:201>"," \t","Success\t||\trenderer unrendered messages _replace_message_in_buffer returns false for unrendered message\t","Fail\t||\trenderer unrendered messages _rerender_part skips if parent message is unrendered\t"," ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: Expected objects to be equal."," Passed in:"," (number) 9"," Expected:"," (number) 15"," "," stack traceback:"," \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:222>"," \t","\t","Success: \t33\t","Failed : \t2\t","========================================\t","Tests Failed. Exit: 1\t","✗ Unit tests failed","------------------------------------------------","","======== TEST FAILURES SUMMARY ========","Found 2 failing test(s):","","FAILED TEST: \trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t","FAILED TEST: \trenderer unrendered messages _rerender_part skips if parent message is unrendered\t","","`````","",""],"extmarks":[[1,1,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-23 21:00:24)","OpencodeHint"],[" [msg_a12df6fcc002lSmBoztX2X6eCp]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}],[2,3,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[3,4,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[4,5,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[5,6,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[6,7,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[7,8,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[8,9,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[9,10,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[10,11,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[11,12,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[12,13,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[13,14,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[14,15,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[15,16,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[16,17,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[17,18,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[18,19,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[19,20,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[20,21,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[21,22,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[22,23,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[23,24,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[24,25,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[25,26,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[26,27,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[27,28,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[28,29,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[29,30,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[30,31,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[31,32,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[32,33,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[33,34,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[34,35,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[35,36,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[36,37,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[37,38,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[38,39,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[39,40,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[40,41,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[41,42,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[42,43,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[43,44,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[44,45,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[45,46,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[46,47,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[47,48,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[48,49,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[49,50,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[50,51,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[51,52,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[52,53,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[53,54,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[54,55,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[55,56,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[56,57,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[57,58,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[58,59,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[59,60,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[60,61,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[61,62,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[62,63,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[63,64,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[64,65,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[65,66,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[66,67,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[67,68,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[68,69,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[69,70,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[70,71,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[71,72,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[72,73,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[73,74,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[74,75,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[75,76,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[76,77,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[77,78,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[78,79,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[79,80,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[80,81,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[81,82,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[82,83,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[83,84,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[84,85,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[85,86,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[86,87,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[87,88,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[88,89,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[89,90,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[90,91,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[91,92,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[92,93,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[93,94,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[94,95,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[95,96,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[96,97,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[97,98,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[98,99,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[99,100,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[100,101,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[101,102,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[102,103,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[103,104,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[104,105,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[105,106,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[106,107,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[107,108,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[108,109,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[109,110,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[110,111,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[111,112,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[112,113,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[113,114,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[114,115,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[115,116,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[116,117,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[117,118,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[118,119,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[119,120,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[120,121,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[121,122,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[122,123,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[123,124,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[124,125,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[125,126,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[126,127,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[127,128,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[128,129,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[129,130,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[130,131,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[131,132,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[132,133,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[133,134,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[134,135,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[135,136,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[136,137,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[137,138,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[138,139,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[139,140,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[140,141,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[141,142,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[142,143,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[143,144,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[144,145,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[145,146,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[146,147,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[147,148,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[148,149,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[149,150,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[150,151,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[151,152,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[152,153,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[153,154,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[154,155,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[155,156,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[156,157,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[157,158,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[158,159,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[159,160,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[160,161,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[161,162,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[162,163,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[163,164,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[164,165,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[165,166,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[166,167,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[167,168,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[168,169,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[169,170,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[170,171,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[171,172,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[172,173,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[173,174,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[174,175,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[175,176,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[176,177,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[177,178,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[178,179,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[179,180,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[180,181,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[181,182,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[182,183,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[183,184,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[184,185,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[185,186,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[186,187,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[187,188,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[188,189,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[189,190,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[190,191,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[191,192,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[192,193,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[193,194,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[194,195,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[195,196,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[196,197,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[197,198,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[198,199,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[199,200,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[200,201,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[201,202,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[202,203,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[203,204,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[204,205,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[205,206,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[206,207,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[207,208,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[208,209,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[209,210,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[210,211,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[211,212,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[212,213,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[213,214,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[214,215,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[215,216,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[216,217,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[217,218,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[218,219,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[219,220,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[220,221,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[221,222,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[222,223,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[223,224,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[224,225,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[225,226,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[226,227,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[227,228,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[228,229,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[229,230,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[230,231,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[231,232,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[232,233,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[233,234,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[234,235,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[235,236,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[236,237,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[237,238,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[238,239,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[239,240,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[240,241,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[241,242,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[242,243,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[243,244,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[244,245,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[245,246,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[246,247,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[247,248,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[248,249,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[249,250,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[250,251,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[251,252,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[252,253,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[253,254,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[254,255,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[255,256,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[256,257,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[257,258,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[258,259,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[259,260,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[260,261,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[261,262,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[262,263,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[263,264,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[264,265,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[265,266,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[266,267,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[267,268,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[268,269,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[269,270,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[270,271,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[271,272,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[272,273,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[273,274,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[274,275,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[275,276,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[276,277,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[277,278,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[278,279,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[279,280,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[280,281,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[281,282,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[282,283,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[283,284,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[284,285,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[285,286,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[286,287,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[287,288,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[288,289,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[289,290,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[290,291,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[291,292,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[292,293,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[293,294,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[294,295,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[295,296,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[296,297,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[297,298,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[298,299,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[299,300,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[300,301,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[301,302,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[302,303,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[303,304,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[304,305,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[305,306,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[306,307,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[307,308,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[308,309,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[309,310,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[310,311,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[311,312,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[312,313,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[313,314,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[314,315,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[315,316,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[316,317,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[317,318,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[318,319,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[319,320,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[320,321,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[321,322,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[322,323,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[323,324,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[324,325,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[325,326,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[326,327,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[327,328,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[328,329,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[329,330,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[330,331,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[331,332,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[332,333,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[333,334,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[334,335,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[335,336,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[336,337,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[337,338,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[338,339,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[339,340,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[340,341,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[341,342,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[342,343,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[343,344,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[344,345,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[345,346,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[346,347,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[347,348,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[348,349,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[349,350,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[350,351,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[351,352,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[352,353,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[353,354,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[354,355,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[355,356,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[356,357,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[357,358,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[358,359,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[359,360,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[360,361,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[361,362,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[362,363,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[363,364,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[364,365,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[365,366,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[366,367,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[367,368,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[368,369,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[369,370,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[370,371,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[371,372,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[372,373,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[373,374,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[374,375,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[375,376,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[376,377,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[377,378,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[378,379,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[379,380,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[380,381,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[381,382,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[382,383,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[383,384,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[384,385,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[385,386,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[386,387,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[387,388,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[388,389,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[389,390,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[390,391,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[391,392,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[392,393,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[393,394,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[394,395,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[395,396,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[396,397,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[397,398,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[398,399,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[399,400,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[400,401,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[401,402,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[402,403,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[403,404,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[404,405,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[405,406,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[406,407,0,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}]],"timestamp":1766432122,"actions":[]} \ No newline at end of file +{ + "lines": [ + "----", + "", + "", + "** run** `Run all tests to verify refactoring`", + "", + "`````bash", + "> cd /Users/cam/Dev/neovim-dev/opencode.nvim && ./run_tests.sh", + "", + "Running tests for opencode.nvim", + "------------------------------------------------", + "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/minimal/plugin_spec.lua\t", + "Success\t||\topencode.nvim plugin loads the plugin without errors\t", + "Success\t||\topencode.nvim plugin can be set up with custom config\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "✓ Minimal tests passed", + "------------------------------------------------", + "Starting...Scheduling: ./tests/unit/timer_spec.lua", + "Scheduling: ./tests/unit/server_job_spec.lua", + "Scheduling: ./tests/unit/config_spec.lua", + "Scheduling: ./tests/unit/api_spec.lua", + "Scheduling: ./tests/unit/event_manager_spec.lua", + "Scheduling: ./tests/unit/init_spec.lua", + "Scheduling: ./tests/unit/state_spec.lua", + "Scheduling: ./tests/unit/id_spec.lua", + "Scheduling: ./tests/unit/api_client_spec.lua", + "Scheduling: ./tests/unit/context_spec.lua", + "Scheduling: ./tests/unit/session_spec.lua", + "Scheduling: ./tests/unit/config_file_spec.lua", + "Scheduling: ./tests/unit/renderer_spec.lua", + "Scheduling: ./tests/unit/opencode_server_spec.lua", + "Scheduling: ./tests/unit/core_spec.lua", + "Scheduling: ./tests/unit/render_state_spec.lua", + "Scheduling: ./tests/unit/snapshot_spec.lua", + "Scheduling: ./tests/unit/keymap_spec.lua", + "Scheduling: ./tests/unit/util_spec.lua", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/server_job_spec.lua\t", + "Success\t||\tserver_job exposes expected public functions\t", + "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", + "Success\t||\tserver_job call_api rejects on non 2xx\t", + "Success\t||\tserver_job stream_api forwards chunks\t", + "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/util_spec.lua\t", + "Success\t||\tutil.parse_dot_args parses flat booleans\t", + "Success\t||\tutil.parse_dot_args parses nested dot notation\t", + "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", + "Success\t||\tutil.parse_dot_args parses numbers\t", + "Success\t||\tutil.parse_dot_args handles empty string\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_client_spec.lua\t", + "Success\t||\tapi_client should create a new client instance\t", + "Success\t||\tapi_client should remove trailing slash from base_url\t", + "Success\t||\tapi_client should create client using create factory function\t", + "Success\t||\tapi_client should have all expected API methods\t", + "Success\t||\tapi_client should construct URLs correctly with query parameters\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/session_spec.lua\t", + "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", + "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", + "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", + "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", + "Success\t||\topencode.session get_by_name returns the session with matching ID\t", + "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", + "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", + "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", + "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", + "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", + "Success\t||\topencode.session get_messages returns nil when session is nil\t", + "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", + "Success\t||\topencode.session get_messages returns messages with their parts\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_file_spec.lua\t", + "Success\t||\tconfig_file.setup lazily loads config when accessed\t", + "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", + "Success\t||\tconfig_file.setup get_opencode_project returns project\t", + "\t", + "Success: \t3\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_spec.lua\t", + "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", + "Success\t||\topencode.api setup registers all commands\t", + "Success\t||\topencode.api setup sets up command functions that call the correct core functions\t", + "Success\t||\topencode.api Lua API provides callable functions that match commands\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/id_spec.lua\t", + "Success\t||\tID module should generate ascending session IDs\t", + "Success\t||\tID module should generate descending message IDs\t", + "Success\t||\tID module should validate given IDs correctly\t", + "Success\t||\tID module should throw error for invalid given IDs\t", + "Success\t||\tID module should validate schemas correctly\t", + "Success\t||\tID module should return available prefixes\t", + "Success\t||\tID module should generate IDs with correct length structure\t", + "\t", + "Success: \t7\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/render_state_spec.lua\t", + "Success\t||\tRenderState new and reset creates a new instance\t", + "Success\t||\tRenderState new and reset resets to empty state\t", + "Success\t||\tRenderState set_message sets a new message\t", + "Success\t||\tRenderState set_message updates line index for message\t", + "Success\t||\tRenderState set_message updates existing message\t", + "Success\t||\tRenderState set_part sets a new part\t", + "Success\t||\tRenderState set_part updates line index for part\t", + "Success\t||\tRenderState set_part initializes actions array\t", + "Success\t||\tRenderState get_part_at_line returns part at line\t", + "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", + "Success\t||\tRenderState get_message_at_line returns message at line\t", + "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", + "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", + "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", + "Success\t||\tRenderState actions adds actions to part\t", + "Success\t||\tRenderState actions adds actions with offset\t", + "Success\t||\tRenderState actions clears actions for part\t", + "Success\t||\tRenderState actions gets actions at line\t", + "Success\t||\tRenderState actions gets all actions from all parts\t", + "Success\t||\tRenderState update_part_lines updates part line positions\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", + "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", + "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", + "Success\t||\tRenderState remove_part clears line index for removed part\t", + "Success\t||\tRenderState remove_part returns false for non-existent part\t", + "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", + "Success\t||\tRenderState remove_message clears line index for removed message\t", + "Success\t||\tRenderState remove_message returns false for non-existent message\t", + "Success\t||\tRenderState remove_message removes unrendered message without shifting\t", + "Success\t||\tRenderState shift_all does nothing when delta is 0\t", + "Success\t||\tRenderState shift_all shifts content at or after from_line\t", + "Success\t||\tRenderState shift_all shifts actions with parts\t", + "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", + "Success\t||\tRenderState shift_all invalidates index when content shifted\t", + "Success\t||\tRenderState shift_all exits early when content found before from_line\t", + "Success\t||\tRenderState update_part_data updates part reference\t", + "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", + "Success\t||\tRenderState get_unrendered_message_ids returns empty list when no unrendered messages\t", + "Success\t||\tRenderState get_unrendered_message_ids returns list of unrendered message IDs\t", + "Success\t||\tRenderState get_unrendered_message_ids returns sorted list of message IDs\t", + "\t", + "Success: \t41\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/init_spec.lua\t", + "Success\t||\topencode has setup function in the public API\t", + "Success\t||\topencode main module can be required without errors\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/snapshot_spec.lua\t", + "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", + "Success\t||\tsnapshot.restore notifies error if no active session\t", + "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", + "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/event_manager_spec.lua\t", + "Success\t||\tEventManager should create a new instance\t", + "Success\t||\tEventManager should subscribe and emit events\t", + "Success\t||\tEventManager should handle multiple subscribers\t", + "Success\t||\tEventManager should unsubscribe correctly\t", + "Success\t||\tEventManager should track subscriber count\t", + "Success\t||\tEventManager should list event names\t", + "Success\t||\tEventManager should handle starting and stopping\t", + "Success\t||\tEventManager should not start multiple times\t", + "\t", + "Success: \t8\t", + "Failed : \t0\t", + "========================================\t", + "Error detected while processing command line:", + "opencode command not found - please install and configure opencode before using this plugin", + "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/core_spec.lua\t", + "Success\t||\topencode.core open creates windows if they don't exist\t", + "Success\t||\topencode.core open handles new session properly\t", + "Success\t||\topencode.core open focuses the appropriate window\t", + "Success\t||\topencode.core select_session filters sessions by description and parentID\t", + "Success\t||\topencode.core send_message sends a message via api_client\t", + "Success\t||\topencode.core send_message creates new session when none active\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", + "\t", + "Success: \t10\t", + "Failed : \t0\t", + "========================================\t", + "File not added to context. Could not read.", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/context_spec.lua\t", + "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", + "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", + "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", + "Success\t||\textract_legacy_tag extracts content between tags\t", + "Success\t||\textract_legacy_tag returns nil if tag not found\t", + "Success\t||\tformat_message returns a parts array with prompt as first part\t", + "Success\t||\tformat_message includes mentioned_files and subagents\t", + "Success\t||\tdelta_context removes current_file if unchanged\t", + "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/opencode_server_spec.lua\t", + "Success\t||\topencode.opencode_server creates a new server object\t", + "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", + "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", + "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", + "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_spec.lua\t", + "Success\t||\topencode.config uses default values when no options are provided\t", + "Success\t||\topencode.config merges user options with defaults\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/keymap_spec.lua\t", + "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", + "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", + "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", + "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", + "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", + "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", + "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", + "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", + "Success\t||\topencode.keymap setup_permisson_keymap sets up permission keymaps when there is a current permission\t", + "Success\t||\topencode.keymap setup_permisson_keymap should delete existing permission keymaps if no current permission exists after being set\t", + "Success\t||\topencode.keymap setup_permisson_keymap does not set permission keymaps when there is no current permission\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/state_spec.lua\t", + "Success\t||\topencode.state (observable) notifies listeners on key change\t", + "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", + "Success\t||\topencode.state (observable) can unregister listeners\t", + "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/timer_spec.lua\t", + "Success\t||\tTimer Timer.new creates a new timer with required options\t", + "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", + "Success\t||\tTimer Timer.new stores optional parameters\t", + "Success\t||\tTimer Timer:start starts a repeating timer\t", + "Success\t||\tTimer Timer:start starts a one-shot timer\t", + "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", + "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", + "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", + "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", + "Success\t||\tTimer Timer:start throws error when timer creation fails\t", + "Success\t||\tTimer Timer:stop stops a running timer\t", + "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", + "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", + "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", + "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", + "Success\t||\tTimer Timer:is_running returns true when timer is running\t", + "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", + "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", + "Success\t||\tTimer Integration tests can restart a stopped timer\t", + "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua\t", + "Success\t||\trenderer replays api-error correctly (event-by-event)\t", + "Success\t||\trenderer replays api-error correctly (session)\t", + "Success\t||\trenderer replays diff correctly (event-by-event)\t", + "Success\t||\trenderer replays diff correctly (session)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event)\t", + "Success\t||\trenderer replays permission-denied correctly (session)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event)\t", + "Success\t||\trenderer replays permission correctly (event-by-event)\t", + "Success\t||\trenderer replays permission correctly (session)\t", + "Success\t||\trenderer replays planning correctly (event-by-event)\t", + "Success\t||\trenderer replays planning correctly (session)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event)\t", + "Success\t||\trenderer replays redo-all correctly (session)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event)\t", + "Success\t||\trenderer replays redo-once correctly (session)\t", + "Success\t||\trenderer replays revert correctly (event-by-event)\t", + "Success\t||\trenderer replays revert correctly (session)\t", + "Success\t||\trenderer replays selection correctly (event-by-event)\t", + "Success\t||\trenderer replays selection correctly (session)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event)\t", + "Success\t||\trenderer replays simple-session correctly (session)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event)\t", + "Success\t||\trenderer replays tool-invalid correctly (session)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event)\t", + "Success\t||\trenderer replays updating-text correctly (session)\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns true for unrendered message\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns false for rendered message\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns false for nil\t", + "Fail\t||\trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", + " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: Expected objects to not be the same.", + " Passed in:", + " (nil)", + " Did not expect:", + " type nil", + " ", + " stack traceback:", + " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:201>", + " \t", + "Success\t||\trenderer unrendered messages _replace_message_in_buffer returns false for unrendered message\t", + "Fail\t||\trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", + " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: Expected objects to be equal.", + " Passed in:", + " (number) 9", + " Expected:", + " (number) 15", + " ", + " stack traceback:", + " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:222>", + " \t", + "\t", + "Success: \t33\t", + "Failed : \t2\t", + "========================================\t", + "Tests Failed. Exit: 1\t", + "✗ Unit tests failed", + "------------------------------------------------", + "", + "======== TEST FAILURES SUMMARY ========", + "Found 2 failing test(s):", + "", + "FAILED TEST: \trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", + "FAILED TEST: \trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", + "", + "`````", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-23 21:00:24)", "OpencodeHint"], + [" [msg_a12df6fcc002lSmBoztX2X6eCp]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 3, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 4, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 5, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 6, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 7, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 8, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 8, + 9, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 10, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 11, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 11, + 12, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 12, + 13, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 13, + 14, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 14, + 15, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 15, + 16, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 16, + 17, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 17, + 18, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 18, + 19, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 19, + 20, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 20, + 21, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 21, + 22, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 22, + 23, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 23, + 24, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 24, + 25, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 25, + 26, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 26, + 27, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 27, + 28, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 28, + 29, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 29, + 30, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 30, + 31, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 31, + 32, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 32, + 33, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 33, + 34, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 34, + 35, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 35, + 36, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 36, + 37, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 37, + 38, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 38, + 39, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 39, + 40, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 40, + 41, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 41, + 42, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 42, + 43, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 43, + 44, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 44, + 45, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 45, + 46, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 46, + 47, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 47, + 48, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 48, + 49, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 49, + 50, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 50, + 51, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 51, + 52, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 52, + 53, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 53, + 54, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 54, + 55, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 55, + 56, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 56, + 57, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 57, + 58, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 58, + 59, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 59, + 60, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 60, + 61, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 61, + 62, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 62, + 63, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 63, + 64, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 64, + 65, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 65, + 66, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 66, + 67, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 67, + 68, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 68, + 69, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 69, + 70, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 70, + 71, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 71, + 72, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 72, + 73, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 73, + 74, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 74, + 75, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 75, + 76, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 76, + 77, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 77, + 78, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 78, + 79, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 79, + 80, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 80, + 81, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 81, + 82, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 82, + 83, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 83, + 84, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 84, + 85, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 85, + 86, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 86, + 87, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 87, + 88, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 88, + 89, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 89, + 90, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 90, + 91, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 91, + 92, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 92, + 93, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 93, + 94, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 94, + 95, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 95, + 96, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 96, + 97, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 97, + 98, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 98, + 99, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 99, + 100, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 100, + 101, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 101, + 102, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 102, + 103, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 103, + 104, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 104, + 105, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 105, + 106, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 106, + 107, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 107, + 108, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 108, + 109, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 109, + 110, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 110, + 111, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 111, + 112, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 112, + 113, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 113, + 114, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 114, + 115, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 115, + 116, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 116, + 117, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 117, + 118, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 118, + 119, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 119, + 120, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 120, + 121, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 121, + 122, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 122, + 123, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 123, + 124, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 124, + 125, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 125, + 126, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 126, + 127, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 127, + 128, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 128, + 129, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 129, + 130, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 130, + 131, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 131, + 132, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 132, + 133, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 133, + 134, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 134, + 135, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 135, + 136, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 136, + 137, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 137, + 138, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 138, + 139, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 139, + 140, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 140, + 141, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 141, + 142, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 142, + 143, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 143, + 144, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 144, + 145, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 145, + 146, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 146, + 147, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 147, + 148, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 148, + 149, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 149, + 150, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 150, + 151, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 151, + 152, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 152, + 153, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 153, + 154, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 154, + 155, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 155, + 156, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 156, + 157, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 157, + 158, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 158, + 159, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 159, + 160, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 160, + 161, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 161, + 162, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 162, + 163, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 163, + 164, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 164, + 165, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 165, + 166, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 166, + 167, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 167, + 168, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 168, + 169, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 169, + 170, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 170, + 171, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 171, + 172, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 172, + 173, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 173, + 174, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 174, + 175, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 175, + 176, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 176, + 177, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 177, + 178, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 178, + 179, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 179, + 180, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 180, + 181, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 181, + 182, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 182, + 183, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 183, + 184, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 184, + 185, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 185, + 186, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 186, + 187, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 187, + 188, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 188, + 189, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 189, + 190, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 190, + 191, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 191, + 192, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 192, + 193, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 193, + 194, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 194, + 195, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 195, + 196, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 196, + 197, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 197, + 198, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 198, + 199, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 199, + 200, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 200, + 201, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 201, + 202, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 202, + 203, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 203, + 204, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 204, + 205, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 205, + 206, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 206, + 207, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 207, + 208, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 208, + 209, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 209, + 210, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 210, + 211, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 211, + 212, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 212, + 213, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 213, + 214, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 214, + 215, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 215, + 216, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 216, + 217, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 217, + 218, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 218, + 219, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 219, + 220, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 220, + 221, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 221, + 222, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 222, + 223, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 223, + 224, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 224, + 225, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 225, + 226, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 226, + 227, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 227, + 228, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 228, + 229, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 229, + 230, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 230, + 231, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 231, + 232, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 232, + 233, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 233, + 234, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 234, + 235, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 235, + 236, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 236, + 237, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 237, + 238, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 238, + 239, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 239, + 240, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 240, + 241, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 241, + 242, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 242, + 243, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 243, + 244, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 244, + 245, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 245, + 246, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 246, + 247, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 247, + 248, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 248, + 249, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 249, + 250, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 250, + 251, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 251, + 252, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 252, + 253, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 253, + 254, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 254, + 255, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 255, + 256, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 256, + 257, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 257, + 258, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 258, + 259, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 259, + 260, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 260, + 261, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 261, + 262, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 262, + 263, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 263, + 264, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 264, + 265, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 265, + 266, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 266, + 267, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 267, + 268, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 268, + 269, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 269, + 270, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 270, + 271, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 271, + 272, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 272, + 273, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 273, + 274, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 274, + 275, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 275, + 276, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 276, + 277, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 277, + 278, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 278, + 279, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 279, + 280, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 280, + 281, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 281, + 282, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 282, + 283, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 283, + 284, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 284, + 285, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 285, + 286, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 286, + 287, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 287, + 288, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 288, + 289, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 289, + 290, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 290, + 291, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 291, + 292, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 292, + 293, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 293, + 294, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 294, + 295, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 295, + 296, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 296, + 297, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 297, + 298, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 298, + 299, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 299, + 300, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 300, + 301, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 301, + 302, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 302, + 303, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 303, + 304, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 304, + 305, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 305, + 306, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 306, + 307, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 307, + 308, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 308, + 309, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 309, + 310, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 310, + 311, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 311, + 312, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 312, + 313, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 313, + 314, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 314, + 315, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 315, + 316, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 316, + 317, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 317, + 318, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 318, + 319, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 319, + 320, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 320, + 321, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 321, + 322, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 322, + 323, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 323, + 324, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 324, + 325, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 325, + 326, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 326, + 327, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 327, + 328, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 328, + 329, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 329, + 330, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 330, + 331, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 331, + 332, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 332, + 333, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 333, + 334, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 334, + 335, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 335, + 336, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 336, + 337, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 337, + 338, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 338, + 339, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 339, + 340, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 340, + 341, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 341, + 342, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 342, + 343, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 343, + 344, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 344, + 345, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 345, + 346, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 346, + 347, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 347, + 348, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 348, + 349, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 349, + 350, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 350, + 351, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 351, + 352, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 352, + 353, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 353, + 354, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 354, + 355, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 355, + 356, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 356, + 357, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 357, + 358, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 358, + 359, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 359, + 360, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 360, + 361, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 361, + 362, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 362, + 363, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 363, + 364, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 364, + 365, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 365, + 366, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 366, + 367, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 367, + 368, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 368, + 369, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 369, + 370, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 370, + 371, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 371, + 372, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 372, + 373, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 373, + 374, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 374, + 375, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 375, + 376, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 376, + 377, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 377, + 378, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 378, + 379, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 379, + 380, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 380, + 381, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 381, + 382, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 382, + 383, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 383, + 384, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 384, + 385, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 385, + 386, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 386, + 387, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 387, + 388, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 388, + 389, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 389, + 390, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 390, + 391, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 391, + 392, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 392, + 393, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 393, + 394, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 394, + 395, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 395, + 396, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 396, + 397, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 397, + 398, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 398, + 399, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 399, + 400, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 400, + 401, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 401, + 402, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 402, + 403, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 403, + 404, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 404, + 405, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 405, + 406, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 406, + 407, + 0, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ] + ], + "timestamp": 1766432122, + "actions": [] +} diff --git a/tests/data/api-abort.expected.json b/tests/data/api-abort.expected.json index 65a6ae56..7998b564 100644 --- a/tests/data/api-abort.expected.json +++ b/tests/data/api-abort.expected.json @@ -1 +1,127 @@ -{"timestamp":1766432123,"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-27 22:44:29)","OpencodeHint"],[" [msg_a27d8299d001nchmBunYlZcPyL]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[4,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[5,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[6,8,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-27 22:44:29)","OpencodeHint"],[" [msg_a27d829f9002sfUFPslHq5P2b4]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}]],"actions":[],"lines":["----","","","can generate 10 numbers?","","[`a-empty.txt`](a-empty.txt)","","----","","","You asked if I can generate 10 numbers, and you referenced reading an empty file (`a-empty.txt`). However, I'm currently in \"plan mode,\" which means I cannot write or modify any files—I'm only allowed to read, observe,","","> [!ERROR] The operation was aborted.","",""]} +{ + "timestamp": 1766432123, + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-27 22:44:29)", "OpencodeHint"], + [" [msg_a27d8299d001nchmBunYlZcPyL]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 4, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 5, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 6, + 8, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-10-27 22:44:29)", "OpencodeHint"], + [" [msg_a27d829f9002sfUFPslHq5P2b4]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ] + ], + "actions": [], + "lines": [ + "----", + "", + "", + "can generate 10 numbers?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "You asked if I can generate 10 numbers, and you referenced reading an empty file (`a-empty.txt`). However, I'm currently in \"plan mode,\" which means I cannot write or modify any files—I'm only allowed to read, observe,", + "", + "> [!ERROR] The operation was aborted.", + "", + "" + ] +} diff --git a/tests/data/api-error.expected.json b/tests/data/api-error.expected.json index 9b1fa101..be5e37a7 100644 --- a/tests/data/api-error.expected.json +++ b/tests/data/api-error.expected.json @@ -1 +1,154 @@ -{"lines":["----","","","test 3","","[`diff-test.txt`](diff-test.txt)","","----","","","This is some sample text","","> [!ERROR] Simulated: tool/file read failed for earlier assistant message","","----","","","> [!ERROR] AI_APICallError: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.","",""],"timestamp":1766432123,"actions":[],"extmarks":[[1,1,0,{"virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 04:44:37)","OpencodeHint"],[" [msg_9ffef0129001CoCrBKemk7DqcU]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[2,2,0,{"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[3,3,0,{"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[4,4,0,{"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[5,5,0,{"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[6,8,0,{"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4-5-20250929","OpencodeHint"],[" (2025-10-20 04:44:37)","OpencodeHint"],[" [msg_9ffef0160001eArLyAssT]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}],[7,15,0,{"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4-5-20250929","OpencodeHint"],[" (2025-10-20 04:44:37)","OpencodeHint"],[" [msg_9ffef0170001s2OM00h2cDa94A]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","ns_id":3,"right_gravity":true}]]} +{ + "lines": [ + "----", + "", + "", + "test 3", + "", + "[`diff-test.txt`](diff-test.txt)", + "", + "----", + "", + "", + "This is some sample text", + "", + "> [!ERROR] Simulated: tool/file read failed for earlier assistant message", + "", + "----", + "", + "", + "> [!ERROR] AI_APICallError: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.", + "", + "" + ], + "timestamp": 1766432123, + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 04:44:37)", "OpencodeHint"], + [" [msg_9ffef0129001CoCrBKemk7DqcU]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 4, + 4, + 0, + { + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 5, + 5, + 0, + { + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 6, + 8, + 0, + { + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4-5-20250929", "OpencodeHint"], + [" (2025-10-20 04:44:37)", "OpencodeHint"], + [" [msg_9ffef0160001eArLyAssT]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ], + [ + 7, + 15, + 0, + { + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4-5-20250929", "OpencodeHint"], + [" (2025-10-20 04:44:37)", "OpencodeHint"], + [" [msg_9ffef0170001s2OM00h2cDa94A]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true + } + ] + ] +} diff --git a/tests/data/cursor_data.expected.json b/tests/data/cursor_data.expected.json index 1d1bd1f7..22ebcc0b 100644 --- a/tests/data/cursor_data.expected.json +++ b/tests/data/cursor_data.expected.json @@ -1 +1,228 @@ -{"lines":["----","","","what is this doing","","[`lua/opencode/context.lua`](lua/opencode/context.lua)","","Line 59:","`````lua","local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')","`````","","----","","","**Explanation**","","- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in  `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.","- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.","- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:"," - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned."," - Otherwise the `is_enabled` value (from `config`) is returned.","- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.","- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.","",""],"timestamp":1769105141,"extmarks":[[1,1,0,{"priority":10,"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-30 17:42:54)","OpencodeHint"],[" [msg_a3637244a001FDRDfoBYVPEGpd]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false}],[2,2,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[3,3,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[4,4,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[5,5,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[6,6,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[7,7,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[8,8,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[9,9,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[10,10,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[11,11,0,{"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_hide":false}],[12,13,0,{"priority":10,"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-30 17:42:56)","OpencodeHint"],[" [msg_a36372b9a001M1lQEix4SK5QE5]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_hide":false}]],"actions":[]} +{ + "lines": [ + "----", + "", + "", + "what is this doing", + "", + "[`lua/opencode/context.lua`](lua/opencode/context.lua)", + "", + "Line 59:", + "`````lua", + "local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')", + "`````", + "", + "----", + "", + "", + "**Explanation**", + "", + "- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in  `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.", + "- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.", + "- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:", + " - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.", + " - Otherwise the `is_enabled` value (from `config`) is returned.", + "- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.", + "- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.", + "", + "" + ], + "timestamp": 1769105141, + "extmarks": [ + [ + 1, + 1, + 0, + { + "priority": 10, + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-30 17:42:54)", "OpencodeHint"], + [" [msg_a3637244a001FDRDfoBYVPEGpd]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 2, + 2, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 3, + 3, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 4, + 4, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 5, + 5, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 6, + 6, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 7, + 7, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 8, + 8, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 9, + 9, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 10, + 10, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 11, + 11, + 0, + { + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ], + [ + 12, + 13, + 0, + { + "priority": 10, + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-30 17:42:56)", "OpencodeHint"], + [" [msg_a36372b9a001M1lQEix4SK5QE5]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_hide": false + } + ] + ], + "actions": [] +} diff --git a/tests/data/cursor_data.json b/tests/data/cursor_data.json index 79b99f71..6ef31f99 100644 --- a/tests/data/cursor_data.json +++ b/tests/data/cursor_data.json @@ -1 +1,705 @@ -[{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1761846174794},"id":"msg_a3637244a001FDRDfoBYVPEGpd","role":"user","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a3637244a001FDRDfoBYVPEGpd","text":"what is this doing","id":"prt_a3637244b0011ts76CwACOFqk7","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a3637244a001FDRDfoBYVPEGpd","synthetic":true,"text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua\"}","id":"prt_a3637244e0015gCGQiQq5R8bn0","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a3637244a001FDRDfoBYVPEGpd","synthetic":true,"text":"\n00001| -- Gathers editor context\n00002| \n00003| local util = require('opencode.util')\n00004| local config = require('opencode.config') --[[@as OpencodeConfig]]\n00005| local state = require('opencode.state')\n00006| \n00007| local M = {}\n00008| \n00009| ---@type OpencodeContext\n00010| M.context = {\n00011| -- current file\n00012| current_file = nil,\n00013| cursor_data = nil,\n00014| \n00015| -- attachments\n00016| mentioned_files = nil,\n00017| selections = {},\n00018| linter_errors = {},\n00019| mentioned_subagents = {},\n00020| }\n00021| \n00022| function M.unload_attachments()\n00023| M.context.mentioned_files = nil\n00024| M.context.selections = nil\n00025| M.context.linter_errors = nil\n00026| end\n00027| \n00028| function M.get_current_buf()\n00029| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00030| if util.is_buf_a_file(curr_buf) then\n00031| return curr_buf, state.last_code_win_before_opencode or vim.api.nvim_get_current_win()\n00032| end\n00033| end\n00034| \n00035| function M.load()\n00036| local buf, win = M.get_current_buf()\n00037| \n00038| if buf then\n00039| local current_file = M.get_current_file(buf)\n00040| local cursor_data = M.get_current_cursor_data(buf, win)\n00041| \n00042| M.context.current_file = current_file\n00043| M.context.cursor_data = cursor_data\n00044| M.context.linter_errors = M.get_diagnostics(buf)\n00045| end\n00046| \n00047| local current_selection = M.get_current_selection()\n00048| if current_selection then\n00049| local selection = M.new_selection(M.context.current_file, current_selection.text, current_selection.lines)\n00050| M.add_selection(selection)\n00051| end\n00052| state.context_updated_at = vim.uv.now()\n00053| end\n00054| \n00055| -- Checks if a context feature is enabled in config or state\n00056| ---@param context_key string\n00057| ---@return boolean\n00058| function M.is_context_enabled(context_key)\n00059| local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')\n00060| local is_state_enabled = vim.tbl_get(state, 'current_context_config', context_key, 'enabled')\n00061| \n00062| if is_state_enabled ~= nil then\n00063| return is_state_enabled\n00064| else\n00065| return is_enabled\n00066| end\n00067| end\n00068| \n00069| function M.get_diagnostics(buf)\n00070| if not M.is_context_enabled('diagnostics') then\n00071| return nil\n00072| end\n00073| \n00074| local diagnostic_conf = config.context and state.current_context_config.diagnostics or config.context.diagnostics\n00075| \n00076| local severity_levels = {}\n00077| if diagnostic_conf.error then\n00078| table.insert(severity_levels, vim.diagnostic.severity.ERROR)\n00079| end\n00080| if diagnostic_conf.warning then\n00081| table.insert(severity_levels, vim.diagnostic.severity.WARN)\n00082| end\n00083| if diagnostic_conf.info then\n00084| table.insert(severity_levels, vim.diagnostic.severity.INFO)\n00085| end\n00086| \n00087| local diagnostics = vim.diagnostic.get(buf, { severity = severity_levels })\n00088| if #diagnostics == 0 then\n00089| return {}\n00090| end\n00091| \n00092| return diagnostics\n00093| end\n00094| \n00095| function M.new_selection(file, content, lines)\n00096| return {\n00097| file = file,\n00098| content = util.indent_code_block(content),\n00099| lines = lines,\n00100| }\n00101| end\n00102| \n00103| function M.add_selection(selection)\n00104| if not M.context.selections then\n00105| M.context.selections = {}\n00106| end\n00107| \n00108| table.insert(M.context.selections, selection)\n00109| \n00110| state.context_updated_at = vim.uv.now()\n00111| end\n00112| \n00113| function M.remove_selection(selection)\n00114| if not M.context.selections then\n00115| return\n00116| end\n00117| \n00118| for i, sel in ipairs(M.context.selections) do\n00119| if sel.file.path == selection.file.path and sel.lines == selection.lines then\n00120| table.remove(M.context.selections, i)\n00121| break\n00122| end\n00123| end\n00124| state.context_updated_at = vim.uv.now()\n00125| end\n00126| \n00127| function M.clear_selections()\n00128| M.context.selections = nil\n00129| end\n00130| \n00131| function M.add_file(file)\n00132| if not M.context.mentioned_files then\n00133| M.context.mentioned_files = {}\n00134| end\n00135| \n00136| local is_file = vim.fn.filereadable(file) == 1\n00137| local is_dir = vim.fn.isdirectory(file) == 1\n00138| if not is_file and not is_dir then\n00139| vim.notify('File not added to context. Could not read.')\n00140| return\n00141| end\n00142| \n00143| file = vim.fn.fnamemodify(file, ':p')\n00144| \n00145| if not vim.tbl_contains(M.context.mentioned_files, file) then\n00146| table.insert(M.context.mentioned_files, file)\n00147| end\n00148| end\n00149| \n00150| function M.remove_file(file)\n00151| file = vim.fn.fnamemodify(file, ':p')\n00152| if not M.context.mentioned_files then\n00153| return\n00154| end\n00155| \n00156| for i, f in ipairs(M.context.mentioned_files) do\n00157| if f == file then\n00158| table.remove(M.context.mentioned_files, i)\n00159| break\n00160| end\n00161| end\n00162| state.context_updated_at = vim.uv.now()\n00163| end\n00164| \n00165| function M.clear_files()\n00166| M.context.mentioned_files = nil\n00167| end\n00168| \n00169| function M.add_subagent(subagent)\n00170| if not M.context.mentioned_subagents then\n00171| M.context.mentioned_subagents = {}\n00172| end\n00173| \n00174| if not vim.tbl_contains(M.context.mentioned_subagents, subagent) then\n00175| table.insert(M.context.mentioned_subagents, subagent)\n00176| end\n00177| state.context_updated_at = vim.uv.now()\n00178| end\n00179| \n00180| function M.remove_subagent(subagent)\n00181| if not M.context.mentioned_subagents then\n00182| return\n00183| end\n00184| \n00185| for i, a in ipairs(M.context.mentioned_subagents) do\n00186| if a == subagent then\n00187| table.remove(M.context.mentioned_subagents, i)\n00188| break\n00189| end\n00190| end\n00191| state.context_updated_at = vim.uv.now()\n00192| end\n00193| \n00194| function M.clear_subagents()\n00195| M.context.mentioned_subagents = nil\n00196| end\n00197| \n00198| ---@param opts? OpencodeContextConfig\n00199| ---@return OpencodeContext\n00200| function M.delta_context(opts)\n00201| opts = opts or config.context\n00202| if opts.enabled == false then\n00203| return {\n00204| current_file = nil,\n00205| mentioned_files = nil,\n00206| selections = nil,\n00207| linter_errors = nil,\n00208| cursor_data = nil,\n00209| mentioned_subagents = nil,\n00210| }\n00211| end\n00212| \n00213| local context = vim.deepcopy(M.context)\n00214| local last_context = state.last_sent_context\n00215| if not last_context then\n00216| return context\n00217| end\n00218| \n00219| -- no need to send file context again\n00220| if\n00221| context.current_file\n00222| and last_context.current_file\n00223| and context.current_file.name == last_context.current_file.name\n00224| then\n00225| context.current_file = nil\n00226| end\n00227| \n00228| -- no need to send subagents again\n00229| if\n00230| context.mentioned_subagents\n00231| and last_context.mentioned_subagents\n00232| and vim.deep_equal(context.mentioned_subagents, last_context.mentioned_subagents)\n00233| then\n00234| context.mentioned_subagents = nil\n00235| end\n00236| \n00237| return context\n00238| end\n00239| \n00240| function M.get_current_file(buf)\n00241| if not M.is_context_enabled('current_file') then\n00242| return nil\n00243| end\n00244| local file = vim.api.nvim_buf_get_name(buf)\n00245| if not file or file == '' or vim.fn.filereadable(file) ~= 1 then\n00246| return nil\n00247| end\n00248| return {\n00249| path = file,\n00250| name = vim.fn.fnamemodify(file, ':t'),\n00251| extension = vim.fn.fnamemodify(file, ':e'),\n00252| }\n00253| end\n00254| \n00255| function M.get_current_cursor_data(buf, win)\n00256| if not M.is_context_enabled('cursor_data') then\n00257| return nil\n00258| end\n00259| \n00260| local cursor_pos = vim.fn.getcurpos(win)\n00261| local cursor_content = vim.trim(vim.api.nvim_buf_get_lines(buf, cursor_pos[2] - 1, cursor_pos[2], false)[1] or '')\n00262| return { line = cursor_pos[2], column = cursor_pos[3], line_content = cursor_content }\n00263| end\n00264| \n00265| function M.get_current_selection()\n00266| if not M.is_context_enabled('selection') then\n00267| return nil\n00268| end\n00269| \n00270| -- Return nil if not in a visual mode\n00271| if not vim.fn.mode():match('[vV\\022]') then\n00272| return nil\n00273| end\n00274| \n00275| -- Save current position and register state\n00276| local current_pos = vim.fn.getpos('.')\n00277| local old_reg = vim.fn.getreg('x')\n00278| local old_regtype = vim.fn.getregtype('x')\n00279| \n00280| -- Capture selection text and position\n00281| vim.cmd('normal! \"xy')\n00282| local text = vim.fn.getreg('x')\n00283| \n00284| -- Get line numbers\n00285| vim.cmd('normal! `<')\n00286| local start_line = vim.fn.line('.')\n00287| vim.cmd('normal! `>')\n00288| local end_line = vim.fn.line('.')\n00289| \n00290| -- Restore state\n00291| vim.fn.setreg('x', old_reg, old_regtype)\n00292| vim.cmd('normal! gv')\n00293| vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, false, true), 'nx', true)\n00294| vim.fn.setpos('.', current_pos)\n00295| \n00296| if not text or text == '' then\n00297| return nil\n00298| end\n00299| \n00300| return {\n00301| text = text and text:match('[^%s]') and text or nil,\n00302| lines = start_line .. ', ' .. end_line,\n00303| }\n00304| end\n00305| \n00306| local function format_file_part(path, prompt)\n00307| local rel_path = vim.fn.fnamemodify(path, ':~:.')\n00308| local mention = '@' .. rel_path\n00309| local pos = prompt and prompt:find(mention)\n00310| pos = pos and pos - 1 or 0 -- convert to 0-based index\n00311| \n00312| local file_part = { filename = rel_path, type = 'file', mime = 'text/plain', url = 'file://' .. path }\n00313| if prompt then\n00314| file_part.source = {\n00315| path = path,\n00316| type = 'file',\n00317| text = { start = pos, value = mention, ['end'] = pos + #mention },\n00318| }\n00319| end\n00320| return file_part\n00321| end\n00322| \n00323| ---@param selection OpencodeContextSelection\n00324| local function format_selection_part(selection)\n00325| local lang = util.get_markdown_filetype(selection.file and selection.file.name or '') or ''\n00326| \n00327| return {\n00328| type = 'text',\n00329| text = vim.json.encode({\n00330| context_type = 'selection',\n00331| file = selection.file,\n00332| content = string.format('`````%s\\n%s\\n`````', lang, selection.content),\n00333| lines = selection.lines,\n00334| }),\n00335| synthetic = true,\n00336| }\n00337| end\n00338| \n00339| local function format_diagnostics_part(diagnostics)\n00340| return {\n00341| type = 'text',\n00342| text = vim.json.encode({ context_type = 'diagnostics', content = diagnostics }),\n00343| synthetic = true,\n00344| }\n00345| end\n00346| \n00347| local function format_cursor_data_part(cursor_data)\n00348| local buf = (M.get_current_buf() or 0) --[[@as integer]]\n00349| local lang = util.get_markdown_filetype(vim.api.nvim_buf_get_name(buf)) or ''\n00350| return {\n00351| type = 'text',\n00352| text = vim.json.encode({\n00353| context_type = 'cursor-data',\n00354| line = cursor_data.line,\n00355| column = cursor_data.column,\n00356| line_content = string.format('`````%s\\n%s\\n`````', lang, cursor_data.line_content),\n00357| }),\n00358| synthetic = true,\n00359| }\n00360| end\n00361| \n00362| local function format_subagents_part(agent, prompt)\n00363| local mention = '@' .. agent\n00364| local pos = prompt:find(mention)\n00365| pos = pos and pos - 1 or 0 -- convert to 0-based index\n00366| \n00367| return {\n00368| type = 'agent',\n00369| name = agent,\n00370| source = { value = mention, start = pos, ['end'] = pos + #mention },\n00371| }\n00372| end\n00373| \n00374| --- Formats a prompt and context into message with parts for the opencode API\n00375| ---@param prompt string\n00376| ---@param opts? OpencodeContextConfig|nil\n00377| ---@return OpencodeMessagePart[]\n00378| function M.format_message(prompt, opts)\n00379| opts = opts or config.context\n00380| local context = M.delta_context(opts)\n00381| context.prompt = prompt\n00382| \n00383| local parts = { { type = 'text', text = prompt } }\n00384| \n00385| for _, path in ipairs(context.mentioned_files or {}) do\n00386| -- don't resend current file if it's also mentioned\n00387| if not context.current_file or path ~= context.current_file.path then\n00388| table.insert(parts, format_file_part(path, prompt))\n00389| end\n00390| end\n00391| \n00392| for _, sel in ipairs(context.selections or {}) do\n00393| table.insert(parts, format_selection_part(sel))\n00394| end\n00395| \n00396| for _, agent in ipairs(context.mentioned_subagents or {}) do\n00397| table.insert(parts, format_subagents_part(agent, prompt))\n00398| end\n00399| \n00400| if context.current_file then\n00401| table.insert(parts, format_file_part(context.current_file.path))\n00402| end\n00403| \n00404| if context.linter_errors then\n00405| table.insert(parts, format_diagnostics_part(context.linter_errors))\n00406| end\n00407| \n00408| if context.cursor_data then\n00409| table.insert(parts, format_cursor_data_part(context.cursor_data))\n00410| end\n00411| \n00412| return parts\n00413| end\n00414| \n00415| ---@param text string\n00416| ---@param context_type string|nil\n00417| function M.decode_json_context(text, context_type)\n00418| local ok, result = pcall(vim.json.decode, text)\n00419| if not ok or (context_type and result.context_type ~= context_type) then\n00420| return nil\n00421| end\n00422| return result\n00423| end\n00424| \n00425| --- Extracts context from an OpencodeMessage (with parts)\n00426| ---@param message { parts: OpencodeMessagePart[] }\n00427| ---@return { prompt: string, selected_text: string|nil, current_file: string|nil, mentioned_files: string[]|nil}\n00428| function M.extract_from_opencode_message(message)\n00429| local ctx = { prompt = nil, selected_text = nil, current_file = nil }\n00430| \n00431| local handlers = {\n00432| text = function(part)\n00433| ctx.prompt = ctx.prompt or part.text or ''\n00434| end,\n00435| text_context = function(part)\n00436| local json = M.decode_json_context(part.text, 'selection')\n00437| ctx.selected_text = json and json.content or ctx.selected_text\n00438| end,\n00439| file = function(part)\n00440| if not part.source then\n00441| ctx.current_file = part.filename\n00442| end\n00443| end,\n00444| }\n00445| \n00446| for _, part in ipairs(message and message.parts or {}) do\n00447| local handler = handlers[part.type .. (part.synthetic and '_context' or '')]\n00448| if handler then\n00449| handler(part)\n00450| end\n00451| \n00452| if ctx.prompt and ctx.selected_text and ctx.current_file then\n00453| break\n00454| end\n00455| end\n00456| \n00457| return ctx\n00458| end\n00459| \n00460| function M.extract_from_message_legacy(text)\n00461| local current_file = M.extract_legacy_tag('current-file', text)\n00462| local context = {\n00463| prompt = M.extract_legacy_tag('user-query', text) or text,\n00464| selected_text = M.extract_legacy_tag('manually-added-selection', text),\n00465| current_file = current_file and current_file:match('Path: (.+)') or nil,\n00466| }\n00467| return context\n00468| end\n00469| \n00470| function M.extract_legacy_tag(tag, text)\n00471| local start_tag = '<' .. tag .. '>'\n00472| local end_tag = ''\n00473| \n00474| local pattern = vim.pesc(start_tag) .. '(.-)' .. vim.pesc(end_tag)\n00475| local content = text:match(pattern)\n00476| \n00477| if content then\n00478| return vim.trim(content)\n00479| end\n00480| \n00481| -- Fallback to the original method if pattern matching fails\n00482| local query_start = text:find(start_tag)\n00483| local query_end = text:find(end_tag)\n00484| \n00485| if query_start and query_end then\n00486| local query_content = text:sub(query_start + #start_tag, query_end - 1)\n00487| return vim.trim(query_content)\n00488| end\n00489| \n00490| return nil\n00491| end\n00492| \n00493| function M.setup()\n00494| state.subscribe({ 'current_code_buf', 'current_context_config', 'is_opencode_focused' }, function(a)\n00495| M.load()\n00496| end)\n00497| \n00498| vim.api.nvim_create_autocmd('BufWritePost', {\n00499| pattern = '*',\n00500| callback = function(args)\n00501| local buf = args.buf\n00502| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00503| if buf == curr_buf and util.is_buf_a_file(buf) then\n00504| M.load()\n00505| end\n00506| end,\n00507| })\n00508| \n00509| vim.api.nvim_create_autocmd('DiagnosticChanged', {\n00510| pattern = '*',\n00511| callback = function(args)\n00512| local buf = args.buf\n00513| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00514| if buf == curr_buf and util.is_buf_a_file(buf) and M.is_context_enabled('diagnostics') then\n00515| M.load()\n00516| end\n00517| end,\n00518| })\n00519| end\n00520| \n00521| return M\n00522| \n","id":"prt_a3637244e0025IN5Ze5THoeXcU","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua","messageID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"prt_a3637244e003lil8R9cdkNVRup","mime":"text/plain","filename":"lua/opencode/context.lua","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a3637244a001FDRDfoBYVPEGpd","synthetic":true,"text":"{\"column\":1,\"line_content\":\"`````lua\\nlocal is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')\\n`````\",\"context_type\":\"cursor-data\",\"line\":59}","id":"prt_a3637244b002S0euEXp3CXpwDw","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"session.updated","properties":{"info":{"version":"0.15.20","directory":"/home/francis/Projects/_nvim/opencode.nvim","title":"New session - 2025-10-30T17:42:54.734Z","time":{"created":1761846174734,"updated":1761846174807},"id":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","projectID":"29d43526f88157cd4edd071b899dd01f240b771b"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"assistant","time":{"created":1761846176666},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","mode":"build","parentID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"msg_a36372b9a001M1lQEix4SK5QE5","modelID":"gpt-5-mini","providerID":"github-copilot","cost":0,"tokens":{"output":0,"input":0,"reasoning":0,"cache":{"write":0,"read":0}},"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"]}}},{"type":"session.updated","properties":{"info":{"version":"0.15.20","directory":"/home/francis/Projects/_nvim/opencode.nvim","title":"New session - 2025-10-30T17:42:54.734Z","time":{"created":1761846174734,"updated":1761846176690},"summary":{"diffs":[]},"id":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","projectID":"29d43526f88157cd4edd071b899dd01f240b771b"}}},{"type":"message.updated","properties":{"info":{"role":"user","time":{"created":1761846174794},"summary":{"diffs":[]},"id":"msg_a3637244a001FDRDfoBYVPEGpd","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"version":"0.15.20","directory":"/home/francis/Projects/_nvim/opencode.nvim","title":"Analyzing opencode/context.lua cursor-data handling","time":{"created":1761846174734,"updated":1761846178487},"summary":{"diffs":[]},"id":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","projectID":"29d43526f88157cd4edd071b899dd01f240b771b"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","snapshot":"41a7223d54ec54b5d1ef4fd276420230fc159812","id":"prt_a36374121001y0L7HUbCtVdq5o","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"_get"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" ("}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" read"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Con","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" Con"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"]."}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context`","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"`"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" intermediate"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" the"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" value"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" key"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n -","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" -"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"enabled"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" returned"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"from"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":".selection"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":".enabled"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" ("}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":"vim"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" it"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"start":1761846182179},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"},"delta":" nested"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","time":{"end":1761846183728,"start":1761846183728},"text":"**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.","id":"prt_a36374123001u3GzKEsk03I3ZQ","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.part.updated","properties":{"part":{"type":"step-finish","snapshot":"41a7223d54ec54b5d1ef4fd276420230fc159812","id":"prt_a363747330018z5AYX660czh96","messageID":"msg_a36372b9a001M1lQEix4SK5QE5","reason":"stop","cost":0,"tokens":{"output":846,"input":18962,"reasoning":0,"cache":{"write":0,"read":12544}},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"message.updated","properties":{"info":{"role":"assistant","time":{"created":1761846176666},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","mode":"build","parentID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"msg_a36372b9a001M1lQEix4SK5QE5","modelID":"gpt-5-mini","providerID":"github-copilot","cost":0,"tokens":{"output":846,"input":18962,"reasoning":0,"cache":{"write":0,"read":12544}},"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"]}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"assistant","time":{"completed":1761846183769,"created":1761846176666},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","mode":"build","parentID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"msg_a36372b9a001M1lQEix4SK5QE5","modelID":"gpt-5-mini","providerID":"github-copilot","cost":0,"tokens":{"output":846,"input":18962,"reasoning":0,"cache":{"write":0,"read":12544}},"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"]}}},{"type":"message.updated","properties":{"info":{"role":"assistant","time":{"completed":1761846183772,"created":1761846176666},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","mode":"build","parentID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"msg_a36372b9a001M1lQEix4SK5QE5","modelID":"gpt-5-mini","providerID":"github-copilot","cost":0,"tokens":{"output":846,"input":18962,"reasoning":0,"cache":{"write":0,"read":12544}},"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"]}}},{"type":"message.updated","properties":{"info":{"role":"assistant","time":{"completed":1761846183774,"created":1761846176666},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","mode":"build","parentID":"msg_a3637244a001FDRDfoBYVPEGpd","id":"msg_a36372b9a001M1lQEix4SK5QE5","modelID":"gpt-5-mini","providerID":"github-copilot","cost":0,"tokens":{"output":846,"input":18962,"reasoning":0,"cache":{"write":0,"read":12544}},"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"]}}},{"type":"session.updated","properties":{"info":{"version":"0.15.20","directory":"/home/francis/Projects/_nvim/opencode.nvim","title":"Analyzing opencode/context.lua cursor-data handling","time":{"created":1761846174734,"updated":1761846183792},"summary":{"diffs":[]},"id":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS","projectID":"29d43526f88157cd4edd071b899dd01f240b771b"}}},{"type":"message.updated","properties":{"info":{"role":"user","time":{"created":1761846174794},"summary":{"diffs":[]},"id":"msg_a3637244a001FDRDfoBYVPEGpd","sessionID":"ses_5c9c8dbf1ffeKzKcDjZ2llgPVS"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]}] +[ + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1761846174794 }, + "id": "msg_a3637244a001FDRDfoBYVPEGpd", + "role": "user", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a3637244a001FDRDfoBYVPEGpd", + "text": "what is this doing", + "id": "prt_a3637244b0011ts76CwACOFqk7", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a3637244a001FDRDfoBYVPEGpd", + "synthetic": true, + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua\"}", + "id": "prt_a3637244e0015gCGQiQq5R8bn0", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a3637244a001FDRDfoBYVPEGpd", + "synthetic": true, + "text": "\n00001| -- Gathers editor context\n00002| \n00003| local util = require('opencode.util')\n00004| local config = require('opencode.config') --[[@as OpencodeConfig]]\n00005| local state = require('opencode.state')\n00006| \n00007| local M = {}\n00008| \n00009| ---@type OpencodeContext\n00010| M.context = {\n00011| -- current file\n00012| current_file = nil,\n00013| cursor_data = nil,\n00014| \n00015| -- attachments\n00016| mentioned_files = nil,\n00017| selections = {},\n00018| linter_errors = {},\n00019| mentioned_subagents = {},\n00020| }\n00021| \n00022| function M.unload_attachments()\n00023| M.context.mentioned_files = nil\n00024| M.context.selections = nil\n00025| M.context.linter_errors = nil\n00026| end\n00027| \n00028| function M.get_current_buf()\n00029| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00030| if util.is_buf_a_file(curr_buf) then\n00031| return curr_buf, state.last_code_win_before_opencode or vim.api.nvim_get_current_win()\n00032| end\n00033| end\n00034| \n00035| function M.load()\n00036| local buf, win = M.get_current_buf()\n00037| \n00038| if buf then\n00039| local current_file = M.get_current_file(buf)\n00040| local cursor_data = M.get_current_cursor_data(buf, win)\n00041| \n00042| M.context.current_file = current_file\n00043| M.context.cursor_data = cursor_data\n00044| M.context.linter_errors = M.get_diagnostics(buf)\n00045| end\n00046| \n00047| local current_selection = M.get_current_selection()\n00048| if current_selection then\n00049| local selection = M.new_selection(M.context.current_file, current_selection.text, current_selection.lines)\n00050| M.add_selection(selection)\n00051| end\n00052| state.context_updated_at = vim.uv.now()\n00053| end\n00054| \n00055| -- Checks if a context feature is enabled in config or state\n00056| ---@param context_key string\n00057| ---@return boolean\n00058| function M.is_context_enabled(context_key)\n00059| local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')\n00060| local is_state_enabled = vim.tbl_get(state, 'current_context_config', context_key, 'enabled')\n00061| \n00062| if is_state_enabled ~= nil then\n00063| return is_state_enabled\n00064| else\n00065| return is_enabled\n00066| end\n00067| end\n00068| \n00069| function M.get_diagnostics(buf)\n00070| if not M.is_context_enabled('diagnostics') then\n00071| return nil\n00072| end\n00073| \n00074| local diagnostic_conf = config.context and state.current_context_config.diagnostics or config.context.diagnostics\n00075| \n00076| local severity_levels = {}\n00077| if diagnostic_conf.error then\n00078| table.insert(severity_levels, vim.diagnostic.severity.ERROR)\n00079| end\n00080| if diagnostic_conf.warning then\n00081| table.insert(severity_levels, vim.diagnostic.severity.WARN)\n00082| end\n00083| if diagnostic_conf.info then\n00084| table.insert(severity_levels, vim.diagnostic.severity.INFO)\n00085| end\n00086| \n00087| local diagnostics = vim.diagnostic.get(buf, { severity = severity_levels })\n00088| if #diagnostics == 0 then\n00089| return {}\n00090| end\n00091| \n00092| return diagnostics\n00093| end\n00094| \n00095| function M.new_selection(file, content, lines)\n00096| return {\n00097| file = file,\n00098| content = util.indent_code_block(content),\n00099| lines = lines,\n00100| }\n00101| end\n00102| \n00103| function M.add_selection(selection)\n00104| if not M.context.selections then\n00105| M.context.selections = {}\n00106| end\n00107| \n00108| table.insert(M.context.selections, selection)\n00109| \n00110| state.context_updated_at = vim.uv.now()\n00111| end\n00112| \n00113| function M.remove_selection(selection)\n00114| if not M.context.selections then\n00115| return\n00116| end\n00117| \n00118| for i, sel in ipairs(M.context.selections) do\n00119| if sel.file.path == selection.file.path and sel.lines == selection.lines then\n00120| table.remove(M.context.selections, i)\n00121| break\n00122| end\n00123| end\n00124| state.context_updated_at = vim.uv.now()\n00125| end\n00126| \n00127| function M.clear_selections()\n00128| M.context.selections = nil\n00129| end\n00130| \n00131| function M.add_file(file)\n00132| if not M.context.mentioned_files then\n00133| M.context.mentioned_files = {}\n00134| end\n00135| \n00136| local is_file = vim.fn.filereadable(file) == 1\n00137| local is_dir = vim.fn.isdirectory(file) == 1\n00138| if not is_file and not is_dir then\n00139| vim.notify('File not added to context. Could not read.')\n00140| return\n00141| end\n00142| \n00143| file = vim.fn.fnamemodify(file, ':p')\n00144| \n00145| if not vim.tbl_contains(M.context.mentioned_files, file) then\n00146| table.insert(M.context.mentioned_files, file)\n00147| end\n00148| end\n00149| \n00150| function M.remove_file(file)\n00151| file = vim.fn.fnamemodify(file, ':p')\n00152| if not M.context.mentioned_files then\n00153| return\n00154| end\n00155| \n00156| for i, f in ipairs(M.context.mentioned_files) do\n00157| if f == file then\n00158| table.remove(M.context.mentioned_files, i)\n00159| break\n00160| end\n00161| end\n00162| state.context_updated_at = vim.uv.now()\n00163| end\n00164| \n00165| function M.clear_files()\n00166| M.context.mentioned_files = nil\n00167| end\n00168| \n00169| function M.add_subagent(subagent)\n00170| if not M.context.mentioned_subagents then\n00171| M.context.mentioned_subagents = {}\n00172| end\n00173| \n00174| if not vim.tbl_contains(M.context.mentioned_subagents, subagent) then\n00175| table.insert(M.context.mentioned_subagents, subagent)\n00176| end\n00177| state.context_updated_at = vim.uv.now()\n00178| end\n00179| \n00180| function M.remove_subagent(subagent)\n00181| if not M.context.mentioned_subagents then\n00182| return\n00183| end\n00184| \n00185| for i, a in ipairs(M.context.mentioned_subagents) do\n00186| if a == subagent then\n00187| table.remove(M.context.mentioned_subagents, i)\n00188| break\n00189| end\n00190| end\n00191| state.context_updated_at = vim.uv.now()\n00192| end\n00193| \n00194| function M.clear_subagents()\n00195| M.context.mentioned_subagents = nil\n00196| end\n00197| \n00198| ---@param opts? OpencodeContextConfig\n00199| ---@return OpencodeContext\n00200| function M.delta_context(opts)\n00201| opts = opts or config.context\n00202| if opts.enabled == false then\n00203| return {\n00204| current_file = nil,\n00205| mentioned_files = nil,\n00206| selections = nil,\n00207| linter_errors = nil,\n00208| cursor_data = nil,\n00209| mentioned_subagents = nil,\n00210| }\n00211| end\n00212| \n00213| local context = vim.deepcopy(M.context)\n00214| local last_context = state.last_sent_context\n00215| if not last_context then\n00216| return context\n00217| end\n00218| \n00219| -- no need to send file context again\n00220| if\n00221| context.current_file\n00222| and last_context.current_file\n00223| and context.current_file.name == last_context.current_file.name\n00224| then\n00225| context.current_file = nil\n00226| end\n00227| \n00228| -- no need to send subagents again\n00229| if\n00230| context.mentioned_subagents\n00231| and last_context.mentioned_subagents\n00232| and vim.deep_equal(context.mentioned_subagents, last_context.mentioned_subagents)\n00233| then\n00234| context.mentioned_subagents = nil\n00235| end\n00236| \n00237| return context\n00238| end\n00239| \n00240| function M.get_current_file(buf)\n00241| if not M.is_context_enabled('current_file') then\n00242| return nil\n00243| end\n00244| local file = vim.api.nvim_buf_get_name(buf)\n00245| if not file or file == '' or vim.fn.filereadable(file) ~= 1 then\n00246| return nil\n00247| end\n00248| return {\n00249| path = file,\n00250| name = vim.fn.fnamemodify(file, ':t'),\n00251| extension = vim.fn.fnamemodify(file, ':e'),\n00252| }\n00253| end\n00254| \n00255| function M.get_current_cursor_data(buf, win)\n00256| if not M.is_context_enabled('cursor_data') then\n00257| return nil\n00258| end\n00259| \n00260| local cursor_pos = vim.fn.getcurpos(win)\n00261| local cursor_content = vim.trim(vim.api.nvim_buf_get_lines(buf, cursor_pos[2] - 1, cursor_pos[2], false)[1] or '')\n00262| return { line = cursor_pos[2], column = cursor_pos[3], line_content = cursor_content }\n00263| end\n00264| \n00265| function M.get_current_selection()\n00266| if not M.is_context_enabled('selection') then\n00267| return nil\n00268| end\n00269| \n00270| -- Return nil if not in a visual mode\n00271| if not vim.fn.mode():match('[vV\\022]') then\n00272| return nil\n00273| end\n00274| \n00275| -- Save current position and register state\n00276| local current_pos = vim.fn.getpos('.')\n00277| local old_reg = vim.fn.getreg('x')\n00278| local old_regtype = vim.fn.getregtype('x')\n00279| \n00280| -- Capture selection text and position\n00281| vim.cmd('normal! \"xy')\n00282| local text = vim.fn.getreg('x')\n00283| \n00284| -- Get line numbers\n00285| vim.cmd('normal! `<')\n00286| local start_line = vim.fn.line('.')\n00287| vim.cmd('normal! `>')\n00288| local end_line = vim.fn.line('.')\n00289| \n00290| -- Restore state\n00291| vim.fn.setreg('x', old_reg, old_regtype)\n00292| vim.cmd('normal! gv')\n00293| vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, false, true), 'nx', true)\n00294| vim.fn.setpos('.', current_pos)\n00295| \n00296| if not text or text == '' then\n00297| return nil\n00298| end\n00299| \n00300| return {\n00301| text = text and text:match('[^%s]') and text or nil,\n00302| lines = start_line .. ', ' .. end_line,\n00303| }\n00304| end\n00305| \n00306| local function format_file_part(path, prompt)\n00307| local rel_path = vim.fn.fnamemodify(path, ':~:.')\n00308| local mention = '@' .. rel_path\n00309| local pos = prompt and prompt:find(mention)\n00310| pos = pos and pos - 1 or 0 -- convert to 0-based index\n00311| \n00312| local file_part = { filename = rel_path, type = 'file', mime = 'text/plain', url = 'file://' .. path }\n00313| if prompt then\n00314| file_part.source = {\n00315| path = path,\n00316| type = 'file',\n00317| text = { start = pos, value = mention, ['end'] = pos + #mention },\n00318| }\n00319| end\n00320| return file_part\n00321| end\n00322| \n00323| ---@param selection OpencodeContextSelection\n00324| local function format_selection_part(selection)\n00325| local lang = util.get_markdown_filetype(selection.file and selection.file.name or '') or ''\n00326| \n00327| return {\n00328| type = 'text',\n00329| text = vim.json.encode({\n00330| context_type = 'selection',\n00331| file = selection.file,\n00332| content = string.format('`````%s\\n%s\\n`````', lang, selection.content),\n00333| lines = selection.lines,\n00334| }),\n00335| synthetic = true,\n00336| }\n00337| end\n00338| \n00339| local function format_diagnostics_part(diagnostics)\n00340| return {\n00341| type = 'text',\n00342| text = vim.json.encode({ context_type = 'diagnostics', content = diagnostics }),\n00343| synthetic = true,\n00344| }\n00345| end\n00346| \n00347| local function format_cursor_data_part(cursor_data)\n00348| local buf = (M.get_current_buf() or 0) --[[@as integer]]\n00349| local lang = util.get_markdown_filetype(vim.api.nvim_buf_get_name(buf)) or ''\n00350| return {\n00351| type = 'text',\n00352| text = vim.json.encode({\n00353| context_type = 'cursor-data',\n00354| line = cursor_data.line,\n00355| column = cursor_data.column,\n00356| line_content = string.format('`````%s\\n%s\\n`````', lang, cursor_data.line_content),\n00357| }),\n00358| synthetic = true,\n00359| }\n00360| end\n00361| \n00362| local function format_subagents_part(agent, prompt)\n00363| local mention = '@' .. agent\n00364| local pos = prompt:find(mention)\n00365| pos = pos and pos - 1 or 0 -- convert to 0-based index\n00366| \n00367| return {\n00368| type = 'agent',\n00369| name = agent,\n00370| source = { value = mention, start = pos, ['end'] = pos + #mention },\n00371| }\n00372| end\n00373| \n00374| --- Formats a prompt and context into message with parts for the opencode API\n00375| ---@param prompt string\n00376| ---@param opts? OpencodeContextConfig|nil\n00377| ---@return OpencodeMessagePart[]\n00378| function M.format_message(prompt, opts)\n00379| opts = opts or config.context\n00380| local context = M.delta_context(opts)\n00381| context.prompt = prompt\n00382| \n00383| local parts = { { type = 'text', text = prompt } }\n00384| \n00385| for _, path in ipairs(context.mentioned_files or {}) do\n00386| -- don't resend current file if it's also mentioned\n00387| if not context.current_file or path ~= context.current_file.path then\n00388| table.insert(parts, format_file_part(path, prompt))\n00389| end\n00390| end\n00391| \n00392| for _, sel in ipairs(context.selections or {}) do\n00393| table.insert(parts, format_selection_part(sel))\n00394| end\n00395| \n00396| for _, agent in ipairs(context.mentioned_subagents or {}) do\n00397| table.insert(parts, format_subagents_part(agent, prompt))\n00398| end\n00399| \n00400| if context.current_file then\n00401| table.insert(parts, format_file_part(context.current_file.path))\n00402| end\n00403| \n00404| if context.linter_errors then\n00405| table.insert(parts, format_diagnostics_part(context.linter_errors))\n00406| end\n00407| \n00408| if context.cursor_data then\n00409| table.insert(parts, format_cursor_data_part(context.cursor_data))\n00410| end\n00411| \n00412| return parts\n00413| end\n00414| \n00415| ---@param text string\n00416| ---@param context_type string|nil\n00417| function M.decode_json_context(text, context_type)\n00418| local ok, result = pcall(vim.json.decode, text)\n00419| if not ok or (context_type and result.context_type ~= context_type) then\n00420| return nil\n00421| end\n00422| return result\n00423| end\n00424| \n00425| --- Extracts context from an OpencodeMessage (with parts)\n00426| ---@param message { parts: OpencodeMessagePart[] }\n00427| ---@return { prompt: string, selected_text: string|nil, current_file: string|nil, mentioned_files: string[]|nil}\n00428| function M.extract_from_opencode_message(message)\n00429| local ctx = { prompt = nil, selected_text = nil, current_file = nil }\n00430| \n00431| local handlers = {\n00432| text = function(part)\n00433| ctx.prompt = ctx.prompt or part.text or ''\n00434| end,\n00435| text_context = function(part)\n00436| local json = M.decode_json_context(part.text, 'selection')\n00437| ctx.selected_text = json and json.content or ctx.selected_text\n00438| end,\n00439| file = function(part)\n00440| if not part.source then\n00441| ctx.current_file = part.filename\n00442| end\n00443| end,\n00444| }\n00445| \n00446| for _, part in ipairs(message and message.parts or {}) do\n00447| local handler = handlers[part.type .. (part.synthetic and '_context' or '')]\n00448| if handler then\n00449| handler(part)\n00450| end\n00451| \n00452| if ctx.prompt and ctx.selected_text and ctx.current_file then\n00453| break\n00454| end\n00455| end\n00456| \n00457| return ctx\n00458| end\n00459| \n00460| function M.extract_from_message_legacy(text)\n00461| local current_file = M.extract_legacy_tag('current-file', text)\n00462| local context = {\n00463| prompt = M.extract_legacy_tag('user-query', text) or text,\n00464| selected_text = M.extract_legacy_tag('manually-added-selection', text),\n00465| current_file = current_file and current_file:match('Path: (.+)') or nil,\n00466| }\n00467| return context\n00468| end\n00469| \n00470| function M.extract_legacy_tag(tag, text)\n00471| local start_tag = '<' .. tag .. '>'\n00472| local end_tag = ''\n00473| \n00474| local pattern = vim.pesc(start_tag) .. '(.-)' .. vim.pesc(end_tag)\n00475| local content = text:match(pattern)\n00476| \n00477| if content then\n00478| return vim.trim(content)\n00479| end\n00480| \n00481| -- Fallback to the original method if pattern matching fails\n00482| local query_start = text:find(start_tag)\n00483| local query_end = text:find(end_tag)\n00484| \n00485| if query_start and query_end then\n00486| local query_content = text:sub(query_start + #start_tag, query_end - 1)\n00487| return vim.trim(query_content)\n00488| end\n00489| \n00490| return nil\n00491| end\n00492| \n00493| function M.setup()\n00494| state.subscribe({ 'current_code_buf', 'current_context_config', 'is_opencode_focused' }, function(a)\n00495| M.load()\n00496| end)\n00497| \n00498| vim.api.nvim_create_autocmd('BufWritePost', {\n00499| pattern = '*',\n00500| callback = function(args)\n00501| local buf = args.buf\n00502| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00503| if buf == curr_buf and util.is_buf_a_file(buf) then\n00504| M.load()\n00505| end\n00506| end,\n00507| })\n00508| \n00509| vim.api.nvim_create_autocmd('DiagnosticChanged', {\n00510| pattern = '*',\n00511| callback = function(args)\n00512| local buf = args.buf\n00513| local curr_buf = state.current_code_buf or vim.api.nvim_get_current_buf()\n00514| if buf == curr_buf and util.is_buf_a_file(buf) and M.is_context_enabled('diagnostics') then\n00515| M.load()\n00516| end\n00517| end,\n00518| })\n00519| end\n00520| \n00521| return M\n00522| \n", + "id": "prt_a3637244e0025IN5Ze5THoeXcU", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua", + "messageID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "prt_a3637244e003lil8R9cdkNVRup", + "mime": "text/plain", + "filename": "lua/opencode/context.lua", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a3637244a001FDRDfoBYVPEGpd", + "synthetic": true, + "text": "{\"column\":1,\"line_content\":\"`````lua\\nlocal is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')\\n`````\",\"context_type\":\"cursor-data\",\"line\":59}", + "id": "prt_a3637244b002S0euEXp3CXpwDw", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "0.15.20", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "title": "New session - 2025-10-30T17:42:54.734Z", + "time": { "created": 1761846174734, "updated": 1761846174807 }, + "id": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "time": { "created": 1761846176666 }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "mode": "build", + "parentID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "msg_a36372b9a001M1lQEix4SK5QE5", + "modelID": "gpt-5-mini", + "providerID": "github-copilot", + "cost": 0, + "tokens": { "output": 0, "input": 0, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ] + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "0.15.20", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "title": "New session - 2025-10-30T17:42:54.734Z", + "time": { "created": 1761846174734, "updated": 1761846176690 }, + "summary": { "diffs": [] }, + "id": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "time": { "created": 1761846174794 }, + "summary": { "diffs": [] }, + "id": "msg_a3637244a001FDRDfoBYVPEGpd", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "0.15.20", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "title": "Analyzing opencode/context.lua cursor-data handling", + "time": { "created": 1761846174734, "updated": 1761846178487 }, + "summary": { "diffs": [] }, + "id": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "snapshot": "41a7223d54ec54b5d1ef4fd276420230fc159812", + "id": "prt_a36374121001y0L7HUbCtVdq5o", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "_get" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " (" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " read" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Con", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " Con" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "]." + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context`", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "`" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " intermediate" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " the" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " value" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " key" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n -", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " -" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "enabled" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " returned" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "from" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": ".selection" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": ".enabled" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " (" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": "vim" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " it" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "start": 1761846182179 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + }, + "delta": " nested" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "time": { "end": 1761846183728, "start": 1761846183728 }, + "text": "**Explanation**\n\n- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.\n- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.\n- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:\n - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.\n - Otherwise the `is_enabled` value (from `config`) is returned.\n- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.\n- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.", + "id": "prt_a36374123001u3GzKEsk03I3ZQ", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-finish", + "snapshot": "41a7223d54ec54b5d1ef4fd276420230fc159812", + "id": "prt_a363747330018z5AYX660czh96", + "messageID": "msg_a36372b9a001M1lQEix4SK5QE5", + "reason": "stop", + "cost": 0, + "tokens": { "output": 846, "input": 18962, "reasoning": 0, "cache": { "write": 0, "read": 12544 } }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "time": { "created": 1761846176666 }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "mode": "build", + "parentID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "msg_a36372b9a001M1lQEix4SK5QE5", + "modelID": "gpt-5-mini", + "providerID": "github-copilot", + "cost": 0, + "tokens": { "output": 846, "input": 18962, "reasoning": 0, "cache": { "write": 0, "read": 12544 } }, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ] + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "time": { "completed": 1761846183769, "created": 1761846176666 }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "mode": "build", + "parentID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "msg_a36372b9a001M1lQEix4SK5QE5", + "modelID": "gpt-5-mini", + "providerID": "github-copilot", + "cost": 0, + "tokens": { "output": 846, "input": 18962, "reasoning": 0, "cache": { "write": 0, "read": 12544 } }, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ] + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "time": { "completed": 1761846183772, "created": 1761846176666 }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "mode": "build", + "parentID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "msg_a36372b9a001M1lQEix4SK5QE5", + "modelID": "gpt-5-mini", + "providerID": "github-copilot", + "cost": 0, + "tokens": { "output": 846, "input": 18962, "reasoning": 0, "cache": { "write": 0, "read": 12544 } }, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ] + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "time": { "completed": 1761846183774, "created": 1761846176666 }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "mode": "build", + "parentID": "msg_a3637244a001FDRDfoBYVPEGpd", + "id": "msg_a36372b9a001M1lQEix4SK5QE5", + "modelID": "gpt-5-mini", + "providerID": "github-copilot", + "cost": 0, + "tokens": { "output": 846, "input": 18962, "reasoning": 0, "cache": { "write": 0, "read": 12544 } }, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n# AGENTS.md spec\n- Repos often contain AGENTS.md files. These files can appear anywhere within the repository.\n- These files are a way for humans to give you (the agent) instructions or tips for working within the container.\n- Some examples might be: coding conventions, info about how code is organized, or instructions for how to run or test code.\n- Instructions in AGENTS.md files:\n - The scope of an AGENTS.md file is the entire directory tree rooted at the folder that contains it.\n - For every file you touch in the final patch, you must obey instructions in any AGENTS.md file whose scope includes that file.\n - Instructions about code style, structure, naming, etc. apply only to code within the AGENTS.md file's scope, unless the file states otherwise.\n - More-deeply-nested AGENTS.md files take precedence in the case of conflicting instructions.\n - Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.\n- The contents of the AGENTS.md file at the root of the repo and any directories from the CWD up to the root are included with the developer message and don't need to be re-read. When working in a subdirectory of CWD, or a directory outside the CWD, check for any AGENTS.md files that may be applicable.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences, focused on immediate, tangible next steps. (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n- **Exception**: Avoid adding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n\n**Examples:**\n\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll patch the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.\n\nNote that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\nDo not repeat the full contents of the plan after an `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nBefore running a command, consider whether or not you have completed the\nprevious step, and make sure to mark it as completed before moving on to the\nnext step. It may be the case that you complete all steps in your plan after a\nsingle pass of implementation. If this is the case, you can simply mark all the\nplanned steps as completed. Sometimes, you may need to change plans in the\nmiddle of a task: call `todowrite` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.\n\nUse a plan when:\n\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level patches, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n\n- **read-only**: You can only read files.\n- **workspace-write**: You can read files. You can write to files in your workspace folder, but not outside it.\n- **danger-full-access**: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n\n- **restricted**\n- **enabled**\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n\n- **untrusted**: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- **on-failure**: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- **on-request**: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- **never**: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Validating your work\n\nIf the codebase has tests or the ability to build or run, consider using them to verify that your work is complete. \n\nWhen testing, your philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests.\n\nSimilarly, once you're confident in correctness, you can suggest or use formatting commands to ensure that your code is well formatted. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\nBe mindful of whether to run validation commands proactively. In the absence of behavioral guidance:\n\n- When running in non-interactive approval modes like **never** or **on-failure**, proactively run tests, lint and do whatever you need to ensure you've completed the task.\n- When working in interactive approval modes like **untrusted**, or **on-request**, hold off on running tests or lint commands until the user is ready for you to finalize your output, because these commands take time to run and slow down iteration. Instead suggest what you want to do next, and let the user confirm first.\n- When working on test-related tasks, such as adding tests, fixing tests, or reproducing a bug to verify behavior, you may proactively run tests regardless of approval mode. Use your judgement to decide whether this is a test-related task.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As such there's no need to show the full contents of large files you have already written unless the user explicitly asks for them. Similarly, if you've created or modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n\n- Use `-` followed by a space for every bullet.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**File References**\nWhen referencing files in your response, make sure to include the relevant start line and always follow the below rules:\n * Use inline code to make file paths clickable.\n * Each reference should have a stand alone path. Even if it's the same file.\n * Accepted: absolute, workspace‑relative, a/ or b/ diff prefixes, or bare filename/suffix.\n * Line/column (1‑based, optional): :line[:column] or #Lline[Ccolumn] (column defaults to 1).\n * Do not use URIs like file://, vscode://, or https://.\n * Do not provide range of lines\n * Examples: src/app.ts, src/app.ts:42, b/server/index.js#L10, C:\\repo\\project\\main.rs:12:5\n\n**Structure**\n\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n\n# Tool Guidelines\n\n## Shell commands\n\nWhen using the shell, you must adhere to the following guidelines:\n\n- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)\n- Read files in chunks with a max chunk size of 250 lines. Do not use python scripts to attempt to output larger chunks of a file. Command line output will be truncated after 10 kilobytes or 256 lines of output, regardless of the command used.\n\n## `todowrite`\n\nA tool named `todowrite` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.\n\nTo create a new plan, call `todowrite` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).\n\nWhen steps have been completed, use `todowrite` to mark each finished step as\n`completed` and the next step you are working on as `in_progress`. There should\nalways be exactly one `in_progress` step until everything is done. You can mark\nmultiple items as complete in a single `todowrite` call.\n\nIf all steps are complete, ensure you call `todowrite` to mark all steps as `completed`.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Thu Oct 30 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treactive_state.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ] + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "0.15.20", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "title": "Analyzing opencode/context.lua cursor-data handling", + "time": { "created": 1761846174734, "updated": 1761846183792 }, + "summary": { "diffs": [] }, + "id": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "time": { "created": 1761846174794 }, + "summary": { "diffs": [] }, + "id": "msg_a3637244a001FDRDfoBYVPEGpd", + "sessionID": "ses_5c9c8dbf1ffeKzKcDjZ2llgPVS" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/diagnostics.expected.json b/tests/data/diagnostics.expected.json index 92ea0fdf..b4e0e451 100644 --- a/tests/data/diagnostics.expected.json +++ b/tests/data/diagnostics.expected.json @@ -1 +1,8504 @@ -{"lines":["----","","","help me fix the file","","[`lua/opencode/core.lua`](lua/opencode/core.lua)","","**Diagnostics:**  (3)","","----","","","Let's address the three EmmyLua diagnostics at line 130 in  `lua/opencode/core.lua`:","","### 1. `param-type-not-match`: expected `string` but found `string?`","- **Location:** `state.active_session.id` (line 130, col 20)","- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.","- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.","","### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`","- **Location:** `params` argument to `:create_message` (line 130, col 45)","- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.","- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.","","### 3. `need-check-nil`: `state.active_session may be nil`","- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.","","---","","## Step-by-step Fix Plan","","```markdown","- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.","- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.","- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.","```","","Let's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`","","`````lua"," "," params.parts = context.format_message(prompt, opts.context)"," M.before_run(opts)"," "," if not state.active_session or not state.active_session.id then"," vim.notify('No active session to send message to', vim.log.levels.ERROR)"," return"," end"," state.api_client"," :create_message(state.active_session.id, params)"," :and_then(function(response)"," if not response or not response.info or not response.parts then","","`````","","**󰻛 Created Snapshot** `8e790371`","","----","","","Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.","","Now, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.","","Currently, the code is:","```lua","if opts.model then"," local provider, model = opts.model:match('^(.-)/(.+)$')"," params.model = { providerID = provider, modelID = model }"," state.current_model = opts.model","end","```","The expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.","","I'll fix this now.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`","","`````lua"," local params = {}"," "," if opts.model then"," local provider, model = opts.model:match('^(.-)/(.+)$')"," params.model = { providerID = provider, modelID = model }"," params.model = { modelID = model, providerID = provider }"," state.current_model = opts.model"," end"," "," if opts.agent then","","`````","","**󰻛 Created Snapshot** `f33f38a7`","","----","","","Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.","","Now, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.","","I'll run the test suite using the provided script to verify everything is working.","","** run** `Run all tests to verify code changes`","","`````bash","> ./run_tests.sh","","Running tests for opencode.nvim","------------------------------------------------","Starting...Scheduling: ./tests/minimal/plugin_spec.lua","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t","Success\t||\topencode.nvim plugin loads the plugin without errors\t","Success\t||\topencode.nvim plugin can be set up with custom config\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","✓ Minimal tests passed","------------------------------------------------","Starting...Scheduling: ./tests/unit/keymap_spec.lua","Scheduling: ./tests/unit/opencode_server_spec.lua","Scheduling: ./tests/unit/context_completion_spec.lua","Scheduling: ./tests/unit/config_file_spec.lua","Scheduling: ./tests/unit/init_spec.lua","Scheduling: ./tests/unit/state_spec.lua","Scheduling: ./tests/unit/render_state_spec.lua","Scheduling: ./tests/unit/server_job_spec.lua","Scheduling: ./tests/unit/config_spec.lua","Scheduling: ./tests/unit/context_bar_spec.lua","Scheduling: ./tests/unit/renderer_spec.lua","Scheduling: ./tests/unit/api_client_spec.lua","Scheduling: ./tests/unit/core_spec.lua","Scheduling: ./tests/unit/session_spec.lua","Scheduling: ./tests/unit/event_manager_spec.lua","Scheduling: ./tests/unit/api_spec.lua","Scheduling: ./tests/unit/context_spec.lua","Scheduling: ./tests/unit/timer_spec.lua","Scheduling: ./tests/unit/util_spec.lua","Scheduling: ./tests/unit/snapshot_spec.lua","Scheduling: ./tests/unit/id_spec.lua","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t","Success\t||\tcontext completion get_source should return a completion source\t","Success\t||\tcontext completion complete function should return empty table when trigger char does not match\t","Success\t||\tcontext completion complete function should return context items when trigger char matches\t","Success\t||\tcontext completion complete function should include selection items when selections exist\t","Success\t||\tcontext completion complete function should include mentioned files when they exist\t","Success\t||\tcontext completion complete function should include subagent items when they exist\t","Success\t||\tcontext completion complete function should filter items based on input\t","Success\t||\tcontext completion complete function should sort items with available items first\t","Success\t||\tcontext completion on_complete function should handle nil item gracefully\t","Success\t||\tcontext completion on_complete function should handle item without data gracefully\t","Success\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t","Success\t||\tcontext completion on_complete function should remove mentioned file when selected\t","Success\t||\tcontext completion on_complete function should remove subagent when selected\t","Success\t||\tcontext completion on_complete function should remove selection when selection item selected\t","Success\t||\tcontext completion on_complete function should clean up trigger character from current line\t","Success\t||\tcontext completion formatting functions should format diagnostics correctly\t","Success\t||\tcontext completion formatting functions should format cursor data correctly\t","Success\t||\tcontext completion formatting functions should format selection correctly\t","Success\t||\tcontext completion edge cases should handle empty context gracefully\t","Success\t||\tcontext completion edge cases should handle disabled contexts correctly\t","\t","Success: \t20\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t","Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t","Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t","Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t","Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t","Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t","Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t","Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t","Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t","Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t","Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t","Success\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t","Success\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t","Success\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t","Success\t||\topencode.opencode_server creates a new server object\t","Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t","Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t","Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t","Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t","Success\t||\tconfig_file.setup lazily loads config when accessed\t","Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t","Success\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t","Success\t||\tconfig_file.setup get_opencode_project returns project\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t","Success\t||\topencode has setup function in the public API\t","Success\t||\topencode main module can be required without errors\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t","Success\t||\tRenderState new and reset creates a new instance\t","Success\t||\tRenderState new and reset resets to empty state\t","Success\t||\tRenderState set_message sets a new message\t","Success\t||\tRenderState set_message updates line index for message\t","Success\t||\tRenderState set_message updates existing message\t","Success\t||\tRenderState set_part sets a new part\t","Success\t||\tRenderState set_part updates line index for part\t","Success\t||\tRenderState set_part initializes actions array\t","Success\t||\tRenderState get_part_at_line returns part at line\t","Success\t||\tRenderState get_part_at_line returns nil for line without part\t","Success\t||\tRenderState get_message_at_line returns message at line\t","Success\t||\tRenderState get_message_at_line returns nil for line without message\t","Success\t||\tRenderState get_part_by_call_id finds part by call ID\t","Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t","Success\t||\tRenderState actions adds actions to part\t","Success\t||\tRenderState actions adds actions with offset\t","Success\t||\tRenderState actions clears actions for part\t","Success\t||\tRenderState actions gets actions at line\t","Success\t||\tRenderState actions gets all actions from all parts\t","Success\t||\tRenderState update_part_lines updates part line positions\t","Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t","Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t","Success\t||\tRenderState update_part_lines returns false for non-existent part\t","Success\t||\tRenderState remove_part removes part and shifts subsequent content\t","Success\t||\tRenderState remove_part clears line index for removed part\t","Success\t||\tRenderState remove_part returns false for non-existent part\t","Success\t||\tRenderState remove_message removes message and shifts subsequent content\t","Success\t||\tRenderState remove_message clears line index for removed message\t","Success\t||\tRenderState remove_message returns false for non-existent message\t","Success\t||\tRenderState shift_all does nothing when delta is 0\t","Success\t||\tRenderState shift_all shifts content at or after from_line\t","Success\t||\tRenderState shift_all shifts actions with parts\t","Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t","Success\t||\tRenderState shift_all invalidates index when content shifted\t","Success\t||\tRenderState shift_all exits early when content found before from_line\t","Success\t||\tRenderState update_part_data updates part reference\t","Success\t||\tRenderState update_part_data does nothing for non-existent part\t","\t","Success: \t37\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t","Success\t||\tserver_job exposes expected public functions\t","Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t","Success\t||\tserver_job call_api rejects on non 2xx\t","Success\t||\tserver_job stream_api forwards chunks\t","Success\t||\tserver_job ensure_server spawns a new opencode server only once\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t","Success\t||\topencode.config uses default values when no options are provided\t","Success\t||\topencode.config merges user options with defaults\t","\t","Success: \t2\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t","Success\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t","Success\t||\topencode.ui.context_bar setup subscribes to state changes\t","\t","Success: \t9\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t","Success\t||\tapi_client should create a new client instance\t","Success\t||\tapi_client should remove trailing slash from base_url\t","Success\t||\tapi_client should create client using create factory function\t","Success\t||\tapi_client should have all expected API methods\t","Success\t||\tapi_client should construct URLs correctly with query parameters\t","\t","Success: \t5\t","Failed : \t0\t","========================================\t","Error detected while processing command line:","opencode command not found - please install and configure opencode before using this plugin","Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t","Success\t||\topencode.core open creates windows if they don't exist\t","Success\t||\topencode.core open handles new session properly\t","Success\t||\topencode.core open focuses the appropriate window\t","Success\t||\topencode.core select_session filters sessions by description and parentID\t","Success\t||\topencode.core send_message sends a message via api_client\t","Success\t||\topencode.core send_message creates new session when none active\t","Success\t||\topencode.core send_message persist options in state when sending message\t","Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t","Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t","Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t","Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t","\t","Success: \t11\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t","Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t","Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t","Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t","Success\t||\topencode.session get_last_workspace_session handles empty session list\t","Success\t||\topencode.session get_by_name returns the session with matching ID\t","Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t","Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t","Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t","Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t","Success\t||\topencode.session read_json_dir skips invalid JSON files\t","Success\t||\topencode.session get_messages returns nil when session is nil\t","Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t","Success\t||\topencode.session get_messages returns messages with their parts\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t","Success\t||\topencode.state (observable) notifies listeners on key change\t","Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t","Success\t||\topencode.state (observable) can unregister listeners\t","Success\t||\topencode.state (observable) does not notify if value is unchanged\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t","Success\t||\tEventManager should create a new instance\t","Success\t||\tEventManager should subscribe and emit events\t","Success\t||\tEventManager should handle multiple subscribers\t","Success\t||\tEventManager should unsubscribe correctly\t","Success\t||\tEventManager should track subscriber count\t","Success\t||\tEventManager should list event names\t","Success\t||\tEventManager should handle starting and stopping\t","Success\t||\tEventManager should not start multiple times\t","\t","Success: \t8\t","Failed : \t0\t","========================================\t","File not added to context. Could not read.","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t","Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t","Success\t||\textract_from_opencode_message returns nils if message or parts missing\t","Success\t||\textract_from_message_legacy extracts legacy tags from text\t","Success\t||\textract_legacy_tag extracts content between tags\t","Success\t||\textract_legacy_tag returns nil if tag not found\t","Success\t||\tformat_message returns a parts array with prompt as first part\t","Success\t||\tformat_message includes mentioned_files and subagents\t","Success\t||\tdelta_context removes current_file if unchanged\t","Success\t||\tdelta_context removes mentioned_subagents if unchanged\t","Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t","Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t","Success\t||\tadd_file/add_selection/add_subagent adds a selection\t","Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t","\t","Success: \t13\t","Failed : \t0\t","========================================\t",":OpencodeOpenInput is deprecated. Use `:Opencode open input` instead",":OpencodeStop is deprecated. Use `:Opencode cancel` instead",":OpencodeClose is deprecated. Use `:Opencode close` instead","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t","Success\t||\topencode.api commands table contains the expected commands with proper structure\t","Success\t||\topencode.api setup registers the main Opencode command and legacy commands\t","Success\t||\topencode.api setup sets up legacy command functions that route to main command\t","Success\t||\topencode.api Lua API provides callable functions that match commands\t","Success\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t","Success\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t","Success\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t","Success\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t","Success\t||\topencode.api run command argument parsing works with run_new command\t","Success\t||\topencode.api run command argument parsing requires a prompt after prefixes\t","Success\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t","\t","Success: \t11\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t","Success\t||\tutil.parse_dot_args parses flat booleans\t","Success\t||\tutil.parse_dot_args parses nested dot notation\t","Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t","Success\t||\tutil.parse_dot_args parses numbers\t","Success\t||\tutil.parse_dot_args handles empty string\t","Success\t||\tutil.parse_run_args parses no prefixes\t","Success\t||\tutil.parse_run_args parses single agent prefix\t","Success\t||\tutil.parse_run_args parses single model prefix\t","Success\t||\tutil.parse_run_args parses single context prefix\t","Success\t||\tutil.parse_run_args parses multiple prefixes in order\t","Success\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t","Success\t||\tutil.parse_run_args handles empty prompt after prefixes\t","Success\t||\tutil.parse_run_args handles empty string\t","Success\t||\tutil.parse_run_args stops parsing at first non-prefix token\t","\t","Success: \t14\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t","Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t","Success\t||\tsnapshot.restore notifies error if no active session\t","Success\t||\tsnapshot.restore notifies error if read-tree fails\t","Success\t||\tsnapshot.restore notifies error if checkout-index fails\t","\t","Success: \t4\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t","Success\t||\tID module should generate ascending session IDs\t","Success\t||\tID module should generate descending message IDs\t","Success\t||\tID module should validate given IDs correctly\t","Success\t||\tID module should throw error for invalid given IDs\t","Success\t||\tID module should validate schemas correctly\t","Success\t||\tID module should return available prefixes\t","Success\t||\tID module should generate IDs with correct length structure\t","\t","Success: \t7\t","Failed : \t0\t","========================================\t","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t","Success\t||\tTimer Timer.new creates a new timer with required options\t","Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t","Success\t||\tTimer Timer.new stores optional parameters\t","Success\t||\tTimer Timer:start starts a repeating timer\t","Success\t||\tTimer Timer:start starts a one-shot timer\t","Success\t||\tTimer Timer:start passes arguments to on_tick function\t","Success\t||\tTimer Timer:start stops timer when on_tick returns false\t","Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t","Success\t||\tTimer Timer:start stops previous timer before starting new one\t","Success\t||\tTimer Timer:start throws error when timer creation fails\t","Success\t||\tTimer Timer:stop stops a running timer\t","Success\t||\tTimer Timer:stop calls on_stop callback when provided\t","Success\t||\tTimer Timer:stop does nothing when timer is not running\t","Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t","Success\t||\tTimer Timer:is_running returns false when timer is not started\t","Success\t||\tTimer Timer:is_running returns true when timer is running\t","Success\t||\tTimer Timer:is_running returns false after timer is stopped\t","Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t","Success\t||\tTimer Integration tests can restart a stopped timer\t","Success\t||\tTimer Integration tests handles rapid start/stop cycles\t","\t","Success: \t20\t","Failed : \t0\t","========================================\t","Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc","Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO","Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc","Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO","","========================================\t","Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t","Success\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays ansi-codes correctly (session)\t","Success\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays api-abort correctly (session)\t","Success\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays api-error correctly (session)\t","Success\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays cursor_data correctly (session)\t","Success\t||\trenderer replays diff correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays diff correctly (session)\t","Success\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays markdown-codefence correctly (session)\t","Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays mentions-with-ranges correctly (session)\t","Success\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays perf correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays perf correctly (session)\t","Success\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays permission-denied correctly (session)\t","Success\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays permission correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays permission correctly (session)\t","Success\t||\trenderer replays planning correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays planning correctly (session)\t","Success\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays redo-all correctly (session)\t","Success\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays redo-once correctly (session)\t","Success\t||\trenderer replays revert correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays revert correctly (session)\t","Success\t||\trenderer replays selection correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays selection correctly (session)\t","Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays simple-session correctly (session)\t","Success\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays tool-invalid correctly (session)\t","Success\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t","Success\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t","Success\t||\trenderer replays updating-text correctly (session)\t","\t","Success: \t60\t","Failed : \t0\t","========================================\t","✓ Unit tests passed","------------------------------------------------","All tests passed successfully!","","`````","","----","","","All steps are complete and all tests pass! Here’s a summary of what was fixed:","","- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.","- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.","- ✅ Verified the fixes by running the full test suite—no errors or failures.","","Your file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!","",""],"extmarks":[[1,1,0,{"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-11-03 13:34:57)","OpencodeHint"],[" [msg_a49ed91d6001coTsjFq9x6FF5W]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_hide":false}],[2,2,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[3,3,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[4,4,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[5,5,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[6,6,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[7,7,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[8,8,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_hide":false}],[9,10,0,{"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-03 13:34:59)","OpencodeHint"],[" [msg_a49ed9828001mN6CDcWPnGnpHS]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_hide":false}],[10,39,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[11,40,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[12,41,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[13,42,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[14,43,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[15,44,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[16,45,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[17,46,0,{"ns_id":3,"end_col":0,"end_row":47,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffAdd"}],[18,46,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[19,47,0,{"ns_id":3,"end_col":0,"end_row":48,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffAdd"}],[20,47,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[21,48,0,{"ns_id":3,"end_col":0,"end_row":49,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffAdd"}],[22,48,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[23,49,0,{"ns_id":3,"end_col":0,"end_row":50,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffAdd"}],[24,49,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[25,50,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[26,51,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[27,52,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[28,53,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[29,54,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[30,55,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[31,60,0,{"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-03 13:35:06)","OpencodeHint"],[" [msg_a49edb694001syExFxMUW1ik3n]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_hide":false}],[32,78,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[33,79,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[34,80,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[35,81,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[36,82,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[37,83,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[38,84,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[39,85,0,{"ns_id":3,"end_col":0,"end_row":86,"hl_eol":true,"end_right_gravity":false,"virt_text":[["-","OpencodeDiffDelete"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffDelete"}],[40,85,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[41,86,0,{"ns_id":3,"end_col":0,"end_row":87,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":5000,"virt_text_hide":false,"hl_group":"OpencodeDiffAdd"}],[42,86,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[43,87,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[44,88,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[45,89,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[46,90,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[47,91,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[48,92,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[49,97,0,{"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-03 13:35:10)","OpencodeHint"],[" [msg_a49edc4640017DZPFVUPHCm7Ji]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_hide":false}],[50,105,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[51,106,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[52,107,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[53,108,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[54,109,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[55,110,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[56,111,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[57,112,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[58,113,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[59,114,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[60,115,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[61,116,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[62,117,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[63,118,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[64,119,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[65,120,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[66,121,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[67,122,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[68,123,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[69,124,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[70,125,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[71,126,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[72,127,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[73,128,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[74,129,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[75,130,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[76,131,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[77,132,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[78,133,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[79,134,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[80,135,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[81,136,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[82,137,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[83,138,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[84,139,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[85,140,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[86,141,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[87,142,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[88,143,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[89,144,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[90,145,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[91,146,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[92,147,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[93,148,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[94,149,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[95,150,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[96,151,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[97,152,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[98,153,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[99,154,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[100,155,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[101,156,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[102,157,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[103,158,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[104,159,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[105,160,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[106,161,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[107,162,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[108,163,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[109,164,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[110,165,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[111,166,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[112,167,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[113,168,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[114,169,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[115,170,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[116,171,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[117,172,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[118,173,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[119,174,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[120,175,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[121,176,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[122,177,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[123,178,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[124,179,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[125,180,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[126,181,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[127,182,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[128,183,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[129,184,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[130,185,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[131,186,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[132,187,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[133,188,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[134,189,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[135,190,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[136,191,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[137,192,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[138,193,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[139,194,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[140,195,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[141,196,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[142,197,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[143,198,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[144,199,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[145,200,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[146,201,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[147,202,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[148,203,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[149,204,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[150,205,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[151,206,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[152,207,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[153,208,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[154,209,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[155,210,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[156,211,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[157,212,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[158,213,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[159,214,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[160,215,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[161,216,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[162,217,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[163,218,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[164,219,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[165,220,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[166,221,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[167,222,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[168,223,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[169,224,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[170,225,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[171,226,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[172,227,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[173,228,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[174,229,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[175,230,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[176,231,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[177,232,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[178,233,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[179,234,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[180,235,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[181,236,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[182,237,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[183,238,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[184,239,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[185,240,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[186,241,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[187,242,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[188,243,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[189,244,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[190,245,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[191,246,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[192,247,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[193,248,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[194,249,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[195,250,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[196,251,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[197,252,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[198,253,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[199,254,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[200,255,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[201,256,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[202,257,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[203,258,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[204,259,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[205,260,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[206,261,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[207,262,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[208,263,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[209,264,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[210,265,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[211,266,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[212,267,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[213,268,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[214,269,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[215,270,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[216,271,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[217,272,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[218,273,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[219,274,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[220,275,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[221,276,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[222,277,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[223,278,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[224,279,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[225,280,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[226,281,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[227,282,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[228,283,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[229,284,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[230,285,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[231,286,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[232,287,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[233,288,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[234,289,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[235,290,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[236,291,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[237,292,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[238,293,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[239,294,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[240,295,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[241,296,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[242,297,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[243,298,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[244,299,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[245,300,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[246,301,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[247,302,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[248,303,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[249,304,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[250,305,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[251,306,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[252,307,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[253,308,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[254,309,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[255,310,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[256,311,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[257,312,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[258,313,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[259,314,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[260,315,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[261,316,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[262,317,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[263,318,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[264,319,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[265,320,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[266,321,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[267,322,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[268,323,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[269,324,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[270,325,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[271,326,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[272,327,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[273,328,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[274,329,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[275,330,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[276,331,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[277,332,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[278,333,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[279,334,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[280,335,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[281,336,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[282,337,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[283,338,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[284,339,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[285,340,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[286,341,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[287,342,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[288,343,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[289,344,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[290,345,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[291,346,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[292,347,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[293,348,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[294,349,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[295,350,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[296,351,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[297,352,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[298,353,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[299,354,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[300,355,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[301,356,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[302,357,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[303,358,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[304,359,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[305,360,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[306,361,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[307,362,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[308,363,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[309,364,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[310,365,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[311,366,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[312,367,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[313,368,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[314,369,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[315,370,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[316,371,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[317,372,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[318,373,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[319,374,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[320,375,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[321,376,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[322,377,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[323,378,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[324,379,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[325,380,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[326,381,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[327,382,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[328,383,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[329,384,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[330,385,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[331,386,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[332,387,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[333,388,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[334,389,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[335,390,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[336,391,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[337,392,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[338,393,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[339,394,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[340,395,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[341,396,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[342,397,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[343,398,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[344,399,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[345,400,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[346,401,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[347,402,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[348,403,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[349,404,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[350,405,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[351,406,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[352,407,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[353,408,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[354,409,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[355,410,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[356,411,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[357,412,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[358,413,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[359,414,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[360,415,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[361,416,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[362,417,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[363,418,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[364,419,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[365,420,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[366,421,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[367,422,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[368,423,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[369,424,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[370,425,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[371,426,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[372,427,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[373,428,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[374,429,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[375,430,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[376,431,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[377,432,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[378,433,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[379,434,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[380,435,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[381,436,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[382,437,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[383,438,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[384,439,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[385,440,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[386,441,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[387,442,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[388,443,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[389,444,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[390,445,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[391,446,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[392,447,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[393,448,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[394,449,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[395,450,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[396,451,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[397,452,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[398,453,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[399,454,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[400,455,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[401,456,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[402,457,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[403,458,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[404,459,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[405,460,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[406,461,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[407,462,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[408,463,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[409,464,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[410,465,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[411,466,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[412,467,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[413,468,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[414,469,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[415,470,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[416,471,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[417,472,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[418,473,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[419,474,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[420,475,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[421,476,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[422,477,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[423,478,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[424,479,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[425,480,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[426,481,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[427,482,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[428,483,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[429,484,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[430,485,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[431,486,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[432,487,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[433,488,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[434,489,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[435,490,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[436,491,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[437,492,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[438,493,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[439,494,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[440,495,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[441,496,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[442,497,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[443,498,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[444,499,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[445,500,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[446,501,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[447,502,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[448,503,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[449,504,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[450,505,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[451,506,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[452,507,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[453,508,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[454,509,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[455,510,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[456,511,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[457,512,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[458,513,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[459,514,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[460,515,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[461,516,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[462,517,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[463,518,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[464,519,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[465,520,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[466,521,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[467,522,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[468,523,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[469,524,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[470,525,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[471,526,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[472,527,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[473,528,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[474,529,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[475,530,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[476,531,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[477,532,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[478,533,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[479,534,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[480,535,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[481,536,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[482,537,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[483,538,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[484,539,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[485,540,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[486,541,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[487,542,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[488,543,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[489,544,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[490,545,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[491,546,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[492,547,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[493,548,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[494,549,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[495,550,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[496,551,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[497,552,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[498,553,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[499,554,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[500,555,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[501,556,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[502,557,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[503,558,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[504,559,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[505,560,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[506,561,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[507,562,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[508,563,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[509,564,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[510,565,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[511,566,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[512,567,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[513,568,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[514,569,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[515,570,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[516,571,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[517,572,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[518,573,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[519,574,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true,"priority":4096,"virt_text_win_col":-1,"virt_text_hide":false}],[520,577,0,{"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-03 13:35:15)","OpencodeHint"],[" [msg_a49edd87f001eOJDDoq5Vb5hra]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_hide":false}]],"actions":[{"display_line":94,"text":"[R]evert file","range":{"to":94,"from":94},"type":"diff_revert_selected_file","args":["f33f38a70b284207e092c2c578a24e96fdd1bd4d"],"key":"R"},{"display_line":94,"text":"Revert [A]ll","range":{"to":94,"from":94},"type":"diff_revert_all","args":["f33f38a70b284207e092c2c578a24e96fdd1bd4d"],"key":"A"},{"display_line":94,"text":"[D]iff","range":{"to":94,"from":94},"type":"diff_open","args":["f33f38a70b284207e092c2c578a24e96fdd1bd4d"],"key":"D"},{"display_line":57,"text":"[R]evert file","range":{"to":57,"from":57},"type":"diff_revert_selected_file","args":["8e7903714919009004aad8754db0035fb47ecb24"],"key":"R"},{"display_line":57,"text":"Revert [A]ll","range":{"to":57,"from":57},"type":"diff_revert_all","args":["8e7903714919009004aad8754db0035fb47ecb24"],"key":"A"},{"display_line":57,"text":"[D]iff","range":{"to":57,"from":57},"type":"diff_open","args":["8e7903714919009004aad8754db0035fb47ecb24"],"key":"D"}],"timestamp":1769172116} +{ + "lines": [ + "----", + "", + "", + "help me fix the file", + "", + "[`lua/opencode/core.lua`](lua/opencode/core.lua)", + "", + "**Diagnostics:**  (3)", + "", + "----", + "", + "", + "Let's address the three EmmyLua diagnostics at line 130 in  `lua/opencode/core.lua`:", + "", + "### 1. `param-type-not-match`: expected `string` but found `string?`", + "- **Location:** `state.active_session.id` (line 130, col 20)", + "- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.", + "- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.", + "", + "### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`", + "- **Location:** `params` argument to `:create_message` (line 130, col 45)", + "- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.", + "- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.", + "", + "### 3. `need-check-nil`: `state.active_session may be nil`", + "- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.", + "", + "---", + "", + "## Step-by-step Fix Plan", + "", + "```markdown", + "- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", + "- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", + "- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.", + "```", + "", + "Let's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", + "", + "`````lua", + " ", + " params.parts = context.format_message(prompt, opts.context)", + " M.before_run(opts)", + " ", + " if not state.active_session or not state.active_session.id then", + " vim.notify('No active session to send message to', vim.log.levels.ERROR)", + " return", + " end", + " state.api_client", + " :create_message(state.active_session.id, params)", + " :and_then(function(response)", + " if not response or not response.info or not response.parts then", + "", + "`````", + "", + "**󰻛 Created Snapshot** `8e790371`", + "", + "----", + "", + "", + "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.", + "", + "Now, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", + "", + "Currently, the code is:", + "```lua", + "if opts.model then", + " local provider, model = opts.model:match('^(.-)/(.+)$')", + " params.model = { providerID = provider, modelID = model }", + " state.current_model = opts.model", + "end", + "```", + "The expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.", + "", + "I'll fix this now.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", + "", + "`````lua", + " local params = {}", + " ", + " if opts.model then", + " local provider, model = opts.model:match('^(.-)/(.+)$')", + " params.model = { providerID = provider, modelID = model }", + " params.model = { modelID = model, providerID = provider }", + " state.current_model = opts.model", + " end", + " ", + " if opts.agent then", + "", + "`````", + "", + "**󰻛 Created Snapshot** `f33f38a7`", + "", + "----", + "", + "", + "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.", + "", + "Now, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.", + "", + "I'll run the test suite using the provided script to verify everything is working.", + "", + "** run** `Run all tests to verify code changes`", + "", + "`````bash", + "> ./run_tests.sh", + "", + "Running tests for opencode.nvim", + "------------------------------------------------", + "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t", + "Success\t||\topencode.nvim plugin loads the plugin without errors\t", + "Success\t||\topencode.nvim plugin can be set up with custom config\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "✓ Minimal tests passed", + "------------------------------------------------", + "Starting...Scheduling: ./tests/unit/keymap_spec.lua", + "Scheduling: ./tests/unit/opencode_server_spec.lua", + "Scheduling: ./tests/unit/context_completion_spec.lua", + "Scheduling: ./tests/unit/config_file_spec.lua", + "Scheduling: ./tests/unit/init_spec.lua", + "Scheduling: ./tests/unit/state_spec.lua", + "Scheduling: ./tests/unit/render_state_spec.lua", + "Scheduling: ./tests/unit/server_job_spec.lua", + "Scheduling: ./tests/unit/config_spec.lua", + "Scheduling: ./tests/unit/context_bar_spec.lua", + "Scheduling: ./tests/unit/renderer_spec.lua", + "Scheduling: ./tests/unit/api_client_spec.lua", + "Scheduling: ./tests/unit/core_spec.lua", + "Scheduling: ./tests/unit/session_spec.lua", + "Scheduling: ./tests/unit/event_manager_spec.lua", + "Scheduling: ./tests/unit/api_spec.lua", + "Scheduling: ./tests/unit/context_spec.lua", + "Scheduling: ./tests/unit/timer_spec.lua", + "Scheduling: ./tests/unit/util_spec.lua", + "Scheduling: ./tests/unit/snapshot_spec.lua", + "Scheduling: ./tests/unit/id_spec.lua", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t", + "Success\t||\tcontext completion get_source should return a completion source\t", + "Success\t||\tcontext completion complete function should return empty table when trigger char does not match\t", + "Success\t||\tcontext completion complete function should return context items when trigger char matches\t", + "Success\t||\tcontext completion complete function should include selection items when selections exist\t", + "Success\t||\tcontext completion complete function should include mentioned files when they exist\t", + "Success\t||\tcontext completion complete function should include subagent items when they exist\t", + "Success\t||\tcontext completion complete function should filter items based on input\t", + "Success\t||\tcontext completion complete function should sort items with available items first\t", + "Success\t||\tcontext completion on_complete function should handle nil item gracefully\t", + "Success\t||\tcontext completion on_complete function should handle item without data gracefully\t", + "Success\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t", + "Success\t||\tcontext completion on_complete function should remove mentioned file when selected\t", + "Success\t||\tcontext completion on_complete function should remove subagent when selected\t", + "Success\t||\tcontext completion on_complete function should remove selection when selection item selected\t", + "Success\t||\tcontext completion on_complete function should clean up trigger character from current line\t", + "Success\t||\tcontext completion formatting functions should format diagnostics correctly\t", + "Success\t||\tcontext completion formatting functions should format cursor data correctly\t", + "Success\t||\tcontext completion formatting functions should format selection correctly\t", + "Success\t||\tcontext completion edge cases should handle empty context gracefully\t", + "Success\t||\tcontext completion edge cases should handle disabled contexts correctly\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t", + "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", + "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", + "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", + "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", + "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", + "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", + "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", + "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", + "Success\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t", + "Success\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t", + "Success\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t", + "Success\t||\topencode.opencode_server creates a new server object\t", + "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", + "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", + "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", + "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t", + "Success\t||\tconfig_file.setup lazily loads config when accessed\t", + "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", + "Success\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t", + "Success\t||\tconfig_file.setup get_opencode_project returns project\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t", + "Success\t||\topencode has setup function in the public API\t", + "Success\t||\topencode main module can be required without errors\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t", + "Success\t||\tRenderState new and reset creates a new instance\t", + "Success\t||\tRenderState new and reset resets to empty state\t", + "Success\t||\tRenderState set_message sets a new message\t", + "Success\t||\tRenderState set_message updates line index for message\t", + "Success\t||\tRenderState set_message updates existing message\t", + "Success\t||\tRenderState set_part sets a new part\t", + "Success\t||\tRenderState set_part updates line index for part\t", + "Success\t||\tRenderState set_part initializes actions array\t", + "Success\t||\tRenderState get_part_at_line returns part at line\t", + "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", + "Success\t||\tRenderState get_message_at_line returns message at line\t", + "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", + "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", + "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", + "Success\t||\tRenderState actions adds actions to part\t", + "Success\t||\tRenderState actions adds actions with offset\t", + "Success\t||\tRenderState actions clears actions for part\t", + "Success\t||\tRenderState actions gets actions at line\t", + "Success\t||\tRenderState actions gets all actions from all parts\t", + "Success\t||\tRenderState update_part_lines updates part line positions\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", + "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", + "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", + "Success\t||\tRenderState remove_part clears line index for removed part\t", + "Success\t||\tRenderState remove_part returns false for non-existent part\t", + "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", + "Success\t||\tRenderState remove_message clears line index for removed message\t", + "Success\t||\tRenderState remove_message returns false for non-existent message\t", + "Success\t||\tRenderState shift_all does nothing when delta is 0\t", + "Success\t||\tRenderState shift_all shifts content at or after from_line\t", + "Success\t||\tRenderState shift_all shifts actions with parts\t", + "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", + "Success\t||\tRenderState shift_all invalidates index when content shifted\t", + "Success\t||\tRenderState shift_all exits early when content found before from_line\t", + "Success\t||\tRenderState update_part_data updates part reference\t", + "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", + "\t", + "Success: \t37\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t", + "Success\t||\tserver_job exposes expected public functions\t", + "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", + "Success\t||\tserver_job call_api rejects on non 2xx\t", + "Success\t||\tserver_job stream_api forwards chunks\t", + "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t", + "Success\t||\topencode.config uses default values when no options are provided\t", + "Success\t||\topencode.config merges user options with defaults\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t", + "Success\t||\topencode.ui.context_bar setup subscribes to state changes\t", + "\t", + "Success: \t9\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t", + "Success\t||\tapi_client should create a new client instance\t", + "Success\t||\tapi_client should remove trailing slash from base_url\t", + "Success\t||\tapi_client should create client using create factory function\t", + "Success\t||\tapi_client should have all expected API methods\t", + "Success\t||\tapi_client should construct URLs correctly with query parameters\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "Error detected while processing command line:", + "opencode command not found - please install and configure opencode before using this plugin", + "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t", + "Success\t||\topencode.core open creates windows if they don't exist\t", + "Success\t||\topencode.core open handles new session properly\t", + "Success\t||\topencode.core open focuses the appropriate window\t", + "Success\t||\topencode.core select_session filters sessions by description and parentID\t", + "Success\t||\topencode.core send_message sends a message via api_client\t", + "Success\t||\topencode.core send_message creates new session when none active\t", + "Success\t||\topencode.core send_message persist options in state when sending message\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", + "\t", + "Success: \t11\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t", + "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", + "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", + "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", + "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", + "Success\t||\topencode.session get_by_name returns the session with matching ID\t", + "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", + "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", + "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", + "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", + "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", + "Success\t||\topencode.session get_messages returns nil when session is nil\t", + "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", + "Success\t||\topencode.session get_messages returns messages with their parts\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t", + "Success\t||\topencode.state (observable) notifies listeners on key change\t", + "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", + "Success\t||\topencode.state (observable) can unregister listeners\t", + "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t", + "Success\t||\tEventManager should create a new instance\t", + "Success\t||\tEventManager should subscribe and emit events\t", + "Success\t||\tEventManager should handle multiple subscribers\t", + "Success\t||\tEventManager should unsubscribe correctly\t", + "Success\t||\tEventManager should track subscriber count\t", + "Success\t||\tEventManager should list event names\t", + "Success\t||\tEventManager should handle starting and stopping\t", + "Success\t||\tEventManager should not start multiple times\t", + "\t", + "Success: \t8\t", + "Failed : \t0\t", + "========================================\t", + "File not added to context. Could not read.", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t", + "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", + "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", + "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", + "Success\t||\textract_legacy_tag extracts content between tags\t", + "Success\t||\textract_legacy_tag returns nil if tag not found\t", + "Success\t||\tformat_message returns a parts array with prompt as first part\t", + "Success\t||\tformat_message includes mentioned_files and subagents\t", + "Success\t||\tdelta_context removes current_file if unchanged\t", + "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + ":OpencodeOpenInput is deprecated. Use `:Opencode open input` instead", + ":OpencodeStop is deprecated. Use `:Opencode cancel` instead", + ":OpencodeClose is deprecated. Use `:Opencode close` instead", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t", + "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", + "Success\t||\topencode.api setup registers the main Opencode command and legacy commands\t", + "Success\t||\topencode.api setup sets up legacy command functions that route to main command\t", + "Success\t||\topencode.api Lua API provides callable functions that match commands\t", + "Success\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t", + "Success\t||\topencode.api run command argument parsing works with run_new command\t", + "Success\t||\topencode.api run command argument parsing requires a prompt after prefixes\t", + "Success\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t", + "\t", + "Success: \t11\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t", + "Success\t||\tutil.parse_dot_args parses flat booleans\t", + "Success\t||\tutil.parse_dot_args parses nested dot notation\t", + "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", + "Success\t||\tutil.parse_dot_args parses numbers\t", + "Success\t||\tutil.parse_dot_args handles empty string\t", + "Success\t||\tutil.parse_run_args parses no prefixes\t", + "Success\t||\tutil.parse_run_args parses single agent prefix\t", + "Success\t||\tutil.parse_run_args parses single model prefix\t", + "Success\t||\tutil.parse_run_args parses single context prefix\t", + "Success\t||\tutil.parse_run_args parses multiple prefixes in order\t", + "Success\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t", + "Success\t||\tutil.parse_run_args handles empty prompt after prefixes\t", + "Success\t||\tutil.parse_run_args handles empty string\t", + "Success\t||\tutil.parse_run_args stops parsing at first non-prefix token\t", + "\t", + "Success: \t14\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t", + "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", + "Success\t||\tsnapshot.restore notifies error if no active session\t", + "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", + "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t", + "Success\t||\tID module should generate ascending session IDs\t", + "Success\t||\tID module should generate descending message IDs\t", + "Success\t||\tID module should validate given IDs correctly\t", + "Success\t||\tID module should throw error for invalid given IDs\t", + "Success\t||\tID module should validate schemas correctly\t", + "Success\t||\tID module should return available prefixes\t", + "Success\t||\tID module should generate IDs with correct length structure\t", + "\t", + "Success: \t7\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t", + "Success\t||\tTimer Timer.new creates a new timer with required options\t", + "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", + "Success\t||\tTimer Timer.new stores optional parameters\t", + "Success\t||\tTimer Timer:start starts a repeating timer\t", + "Success\t||\tTimer Timer:start starts a one-shot timer\t", + "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", + "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", + "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", + "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", + "Success\t||\tTimer Timer:start throws error when timer creation fails\t", + "Success\t||\tTimer Timer:stop stops a running timer\t", + "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", + "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", + "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", + "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", + "Success\t||\tTimer Timer:is_running returns true when timer is running\t", + "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", + "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", + "Success\t||\tTimer Integration tests can restart a stopped timer\t", + "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t", + "Success\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays ansi-codes correctly (session)\t", + "Success\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays api-abort correctly (session)\t", + "Success\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays api-error correctly (session)\t", + "Success\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays cursor_data correctly (session)\t", + "Success\t||\trenderer replays diff correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays diff correctly (session)\t", + "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays markdown-codefence correctly (session)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays perf correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays perf correctly (session)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission-denied correctly (session)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission correctly (session)\t", + "Success\t||\trenderer replays planning correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays planning correctly (session)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays redo-all correctly (session)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays redo-once correctly (session)\t", + "Success\t||\trenderer replays revert correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays revert correctly (session)\t", + "Success\t||\trenderer replays selection correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays selection correctly (session)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (session)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays tool-invalid correctly (session)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays updating-text correctly (session)\t", + "\t", + "Success: \t60\t", + "Failed : \t0\t", + "========================================\t", + "✓ Unit tests passed", + "------------------------------------------------", + "All tests passed successfully!", + "", + "`````", + "", + "----", + "", + "", + "All steps are complete and all tests pass! Here’s a summary of what was fixed:", + "", + "- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", + "- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.", + "- ✅ Verified the fixes by running the full test suite—no errors or failures.", + "", + "Your file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-11-03 13:34:57)", "OpencodeHint"], + [" [msg_a49ed91d6001coTsjFq9x6FF5W]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 4, + 4, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 5, + 5, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 6, + 6, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 7, + 7, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 8, + 8, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 9, + 10, + 0, + { + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-03 13:34:59)", "OpencodeHint"], + [" [msg_a49ed9828001mN6CDcWPnGnpHS]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 10, + 39, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 11, + 40, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 12, + 41, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 13, + 42, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 14, + 43, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 15, + 44, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 16, + 45, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 17, + 46, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 47, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd" + } + ], + [ + 18, + 46, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 19, + 47, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 48, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd" + } + ], + [ + 20, + 47, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 21, + 48, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 49, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd" + } + ], + [ + 22, + 48, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 23, + 49, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 50, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd" + } + ], + [ + 24, + 49, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 25, + 50, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 26, + 51, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 27, + 52, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 28, + 53, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 29, + 54, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 30, + 55, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 31, + 60, + 0, + { + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-03 13:35:06)", "OpencodeHint"], + [" [msg_a49edb694001syExFxMUW1ik3n]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 32, + 78, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 33, + 79, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 34, + 80, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 35, + 81, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 36, + 82, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 37, + 83, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 38, + 84, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 39, + 85, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 86, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["-", "OpencodeDiffDelete"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffDelete" + } + ], + [ + 40, + 85, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 41, + 86, + 0, + { + "ns_id": 3, + "end_col": 0, + "end_row": 87, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 5000, + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd" + } + ], + [ + 42, + 86, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 43, + 87, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 44, + 88, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 45, + 89, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 46, + 90, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 47, + 91, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 48, + 92, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 49, + 97, + 0, + { + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-03 13:35:10)", "OpencodeHint"], + [" [msg_a49edc4640017DZPFVUPHCm7Ji]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ], + [ + 50, + 105, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 51, + 106, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 52, + 107, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 53, + 108, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 54, + 109, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 55, + 110, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 56, + 111, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 57, + 112, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 58, + 113, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 59, + 114, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 60, + 115, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 61, + 116, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 62, + 117, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 63, + 118, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 64, + 119, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 65, + 120, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 66, + 121, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 67, + 122, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 68, + 123, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 69, + 124, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 70, + 125, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 71, + 126, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 72, + 127, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 73, + 128, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 74, + 129, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 75, + 130, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 76, + 131, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 77, + 132, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 78, + 133, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 79, + 134, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 80, + 135, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 81, + 136, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 82, + 137, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 83, + 138, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 84, + 139, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 85, + 140, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 86, + 141, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 87, + 142, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 88, + 143, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 89, + 144, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 90, + 145, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 91, + 146, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 92, + 147, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 93, + 148, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 94, + 149, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 95, + 150, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 96, + 151, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 97, + 152, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 98, + 153, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 99, + 154, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 100, + 155, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 101, + 156, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 102, + 157, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 103, + 158, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 104, + 159, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 105, + 160, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 106, + 161, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 107, + 162, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 108, + 163, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 109, + 164, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 110, + 165, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 111, + 166, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 112, + 167, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 113, + 168, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 114, + 169, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 115, + 170, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 116, + 171, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 117, + 172, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 118, + 173, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 119, + 174, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 120, + 175, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 121, + 176, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 122, + 177, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 123, + 178, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 124, + 179, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 125, + 180, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 126, + 181, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 127, + 182, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 128, + 183, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 129, + 184, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 130, + 185, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 131, + 186, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 132, + 187, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 133, + 188, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 134, + 189, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 135, + 190, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 136, + 191, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 137, + 192, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 138, + 193, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 139, + 194, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 140, + 195, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 141, + 196, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 142, + 197, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 143, + 198, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 144, + 199, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 145, + 200, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 146, + 201, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 147, + 202, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 148, + 203, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 149, + 204, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 150, + 205, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 151, + 206, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 152, + 207, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 153, + 208, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 154, + 209, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 155, + 210, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 156, + 211, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 157, + 212, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 158, + 213, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 159, + 214, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 160, + 215, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 161, + 216, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 162, + 217, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 163, + 218, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 164, + 219, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 165, + 220, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 166, + 221, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 167, + 222, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 168, + 223, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 169, + 224, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 170, + 225, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 171, + 226, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 172, + 227, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 173, + 228, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 174, + 229, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 175, + 230, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 176, + 231, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 177, + 232, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 178, + 233, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 179, + 234, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 180, + 235, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 181, + 236, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 182, + 237, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 183, + 238, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 184, + 239, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 185, + 240, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 186, + 241, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 187, + 242, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 188, + 243, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 189, + 244, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 190, + 245, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 191, + 246, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 192, + 247, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 193, + 248, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 194, + 249, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 195, + 250, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 196, + 251, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 197, + 252, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 198, + 253, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 199, + 254, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 200, + 255, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 201, + 256, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 202, + 257, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 203, + 258, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 204, + 259, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 205, + 260, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 206, + 261, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 207, + 262, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 208, + 263, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 209, + 264, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 210, + 265, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 211, + 266, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 212, + 267, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 213, + 268, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 214, + 269, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 215, + 270, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 216, + 271, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 217, + 272, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 218, + 273, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 219, + 274, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 220, + 275, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 221, + 276, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 222, + 277, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 223, + 278, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 224, + 279, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 225, + 280, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 226, + 281, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 227, + 282, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 228, + 283, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 229, + 284, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 230, + 285, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 231, + 286, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 232, + 287, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 233, + 288, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 234, + 289, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 235, + 290, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 236, + 291, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 237, + 292, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 238, + 293, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 239, + 294, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 240, + 295, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 241, + 296, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 242, + 297, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 243, + 298, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 244, + 299, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 245, + 300, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 246, + 301, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 247, + 302, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 248, + 303, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 249, + 304, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 250, + 305, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 251, + 306, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 252, + 307, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 253, + 308, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 254, + 309, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 255, + 310, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 256, + 311, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 257, + 312, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 258, + 313, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 259, + 314, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 260, + 315, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 261, + 316, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 262, + 317, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 263, + 318, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 264, + 319, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 265, + 320, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 266, + 321, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 267, + 322, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 268, + 323, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 269, + 324, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 270, + 325, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 271, + 326, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 272, + 327, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 273, + 328, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 274, + 329, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 275, + 330, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 276, + 331, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 277, + 332, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 278, + 333, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 279, + 334, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 280, + 335, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 281, + 336, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 282, + 337, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 283, + 338, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 284, + 339, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 285, + 340, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 286, + 341, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 287, + 342, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 288, + 343, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 289, + 344, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 290, + 345, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 291, + 346, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 292, + 347, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 293, + 348, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 294, + 349, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 295, + 350, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 296, + 351, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 297, + 352, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 298, + 353, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 299, + 354, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 300, + 355, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 301, + 356, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 302, + 357, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 303, + 358, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 304, + 359, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 305, + 360, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 306, + 361, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 307, + 362, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 308, + 363, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 309, + 364, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 310, + 365, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 311, + 366, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 312, + 367, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 313, + 368, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 314, + 369, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 315, + 370, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 316, + 371, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 317, + 372, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 318, + 373, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 319, + 374, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 320, + 375, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 321, + 376, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 322, + 377, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 323, + 378, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 324, + 379, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 325, + 380, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 326, + 381, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 327, + 382, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 328, + 383, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 329, + 384, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 330, + 385, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 331, + 386, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 332, + 387, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 333, + 388, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 334, + 389, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 335, + 390, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 336, + 391, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 337, + 392, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 338, + 393, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 339, + 394, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 340, + 395, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 341, + 396, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 342, + 397, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 343, + 398, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 344, + 399, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 345, + 400, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 346, + 401, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 347, + 402, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 348, + 403, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 349, + 404, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 350, + 405, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 351, + 406, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 352, + 407, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 353, + 408, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 354, + 409, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 355, + 410, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 356, + 411, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 357, + 412, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 358, + 413, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 359, + 414, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 360, + 415, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 361, + 416, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 362, + 417, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 363, + 418, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 364, + 419, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 365, + 420, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 366, + 421, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 367, + 422, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 368, + 423, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 369, + 424, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 370, + 425, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 371, + 426, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 372, + 427, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 373, + 428, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 374, + 429, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 375, + 430, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 376, + 431, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 377, + 432, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 378, + 433, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 379, + 434, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 380, + 435, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 381, + 436, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 382, + 437, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 383, + 438, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 384, + 439, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 385, + 440, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 386, + 441, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 387, + 442, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 388, + 443, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 389, + 444, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 390, + 445, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 391, + 446, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 392, + 447, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 393, + 448, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 394, + 449, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 395, + 450, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 396, + 451, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 397, + 452, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 398, + 453, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 399, + 454, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 400, + 455, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 401, + 456, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 402, + 457, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 403, + 458, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 404, + 459, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 405, + 460, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 406, + 461, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 407, + 462, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 408, + 463, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 409, + 464, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 410, + 465, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 411, + 466, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 412, + 467, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 413, + 468, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 414, + 469, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 415, + 470, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 416, + 471, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 417, + 472, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 418, + 473, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 419, + 474, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 420, + 475, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 421, + 476, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 422, + 477, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 423, + 478, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 424, + 479, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 425, + 480, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 426, + 481, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 427, + 482, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 428, + 483, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 429, + 484, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 430, + 485, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 431, + 486, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 432, + 487, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 433, + 488, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 434, + 489, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 435, + 490, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 436, + 491, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 437, + 492, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 438, + 493, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 439, + 494, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 440, + 495, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 441, + 496, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 442, + 497, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 443, + 498, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 444, + 499, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 445, + 500, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 446, + 501, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 447, + 502, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 448, + 503, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 449, + 504, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 450, + 505, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 451, + 506, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 452, + 507, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 453, + 508, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 454, + 509, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 455, + 510, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 456, + 511, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 457, + 512, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 458, + 513, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 459, + 514, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 460, + 515, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 461, + 516, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 462, + 517, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 463, + 518, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 464, + 519, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 465, + 520, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 466, + 521, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 467, + 522, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 468, + 523, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 469, + 524, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 470, + 525, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 471, + 526, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 472, + 527, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 473, + 528, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 474, + 529, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 475, + 530, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 476, + 531, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 477, + 532, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 478, + 533, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 479, + 534, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 480, + 535, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 481, + 536, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 482, + 537, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 483, + 538, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 484, + 539, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 485, + 540, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 486, + 541, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 487, + 542, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 488, + 543, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 489, + 544, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 490, + 545, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 491, + 546, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 492, + 547, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 493, + 548, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 494, + 549, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 495, + 550, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 496, + 551, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 497, + 552, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 498, + 553, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 499, + 554, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 500, + 555, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 501, + 556, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 502, + 557, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 503, + 558, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 504, + 559, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 505, + 560, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 506, + 561, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 507, + 562, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 508, + 563, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 509, + 564, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 510, + 565, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 511, + 566, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 512, + 567, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 513, + 568, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 514, + 569, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 515, + 570, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 516, + 571, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 517, + 572, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 518, + 573, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 519, + 574, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -1, + "virt_text_hide": false + } + ], + [ + 520, + 577, + 0, + { + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-03 13:35:15)", "OpencodeHint"], + [" [msg_a49edd87f001eOJDDoq5Vb5hra]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_hide": false + } + ] + ], + "actions": [ + { + "display_line": 94, + "text": "[R]evert file", + "range": { "to": 94, "from": 94 }, + "type": "diff_revert_selected_file", + "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], + "key": "R" + }, + { + "display_line": 94, + "text": "Revert [A]ll", + "range": { "to": 94, "from": 94 }, + "type": "diff_revert_all", + "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], + "key": "A" + }, + { + "display_line": 94, + "text": "[D]iff", + "range": { "to": 94, "from": 94 }, + "type": "diff_open", + "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], + "key": "D" + }, + { + "display_line": 57, + "text": "[R]evert file", + "range": { "to": 57, "from": 57 }, + "type": "diff_revert_selected_file", + "args": ["8e7903714919009004aad8754db0035fb47ecb24"], + "key": "R" + }, + { + "display_line": 57, + "text": "Revert [A]ll", + "range": { "to": 57, "from": 57 }, + "type": "diff_revert_all", + "args": ["8e7903714919009004aad8754db0035fb47ecb24"], + "key": "A" + }, + { + "display_line": 57, + "text": "[D]iff", + "range": { "to": 57, "from": 57 }, + "type": "diff_open", + "args": ["8e7903714919009004aad8754db0035fb47ecb24"], + "key": "D" + } + ], + "timestamp": 1769172116 +} diff --git a/tests/data/diagnostics.json b/tests/data/diagnostics.json index a8c6209b..39583961 100644 --- a/tests/data/diagnostics.json +++ b/tests/data/diagnostics.json @@ -1 +1,24213 @@ -[{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176872514},"directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2025-11-03T13:34:32.514Z","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","id":"msg_a49ed91d6001coTsjFq9x6FF5W","role":"user","time":{"created":1762176897494}}}},{"type":"message.part.updated","properties":{"part":{"text":"help me fix the file","id":"prt_a49ed91d70014tkQ4g5HLMkbpH","type":"text","messageID":"msg_a49ed91d6001coTsjFq9x6FF5W","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\"}","id":"prt_a49ed91db0014Vn7x8yXZAkOxO","synthetic":true,"type":"text","messageID":"msg_a49ed91d6001coTsjFq9x6FF5W","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"\n00001| -- This file was written by an automated tool.\n00002| local state = require('opencode.state')\n00003| local context = require('opencode.context')\n00004| local session = require('opencode.session')\n00005| local ui = require('opencode.ui.ui')\n00006| local server_job = require('opencode.server_job')\n00007| local input_window = require('opencode.ui.input_window')\n00008| local util = require('opencode.util')\n00009| local config = require('opencode.config')\n00010| \n00011| local M = {}\n00012| M._abort_count = 0\n00013| \n00014| ---@param parent_id string?\n00015| function M.select_session(parent_id)\n00016| local all_sessions = session.get_all_workspace_sessions() or {}\n00017| local filtered_sessions = vim.tbl_filter(function(s)\n00018| return s.description ~= '' and s ~= nil and s.parentID == parent_id\n00019| end, all_sessions)\n00020| \n00021| ui.select_session(filtered_sessions, function(selected_session)\n00022| if not selected_session then\n00023| if state.windows then\n00024| ui.focus_input()\n00025| end\n00026| return\n00027| end\n00028| state.active_session = selected_session\n00029| if state.windows then\n00030| state.restore_points = {}\n00031| -- Don't need to update either renderer because they subscribe to\n00032| -- session changes\n00033| ui.focus_input()\n00034| else\n00035| M.open()\n00036| end\n00037| end)\n00038| end\n00039| \n00040| ---@param opts? OpenOpts\n00041| function M.open(opts)\n00042| opts = opts or { focus = 'input', new_session = false }\n00043| \n00044| if not state.opencode_server or not state.opencode_server:is_running() then\n00045| state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n00046| end\n00047| \n00048| M.ensure_current_mode()\n00049| \n00050| local are_windows_closed = state.windows == nil\n00051| \n00052| if not require('opencode.ui.ui').is_opencode_focused() then\n00053| require('opencode.context').load()\n00054| end\n00055| \n00056| if are_windows_closed then\n00057| -- Check if whether prompting will be allowed\n00058| local context_module = require('opencode.context')\n00059| local mentioned_files = context_module.context.mentioned_files or {}\n00060| local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n00061| if not allowed then\n00062| vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n00063| end\n00064| \n00065| state.windows = ui.create_windows()\n00066| end\n00067| \n00068| if opts.new_session then\n00069| state.active_session = nil\n00070| state.last_sent_context = nil\n00071| state.active_session = M.create_new_session()\n00072| else\n00073| if not state.active_session then\n00074| state.active_session = session.get_last_workspace_session()\n00075| else\n00076| if not state.display_route and are_windows_closed then\n00077| -- We're not displaying /help or something like that but we have an active session\n00078| -- and the windows were closed so we need to do a full refresh. This mostly happens\n00079| -- when opening the window after having closed it since we're not currently clearing\n00080| -- the session on api.close()\n00081| ui.render_output(false)\n00082| end\n00083| end\n00084| end\n00085| \n00086| if opts.focus == 'input' then\n00087| ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n00088| elseif opts.focus == 'output' then\n00089| ui.focus_output({ restore_position = are_windows_closed })\n00090| end\n00091| state.is_opencode_focused = true\n00092| end\n00093| \n00094| --- Sends a message to the active session, creating one if necessary.\n00095| --- @param prompt string The message prompt to send.\n00096| --- @param opts? SendMessageOpts\n00097| function M.send_message(prompt, opts)\n00098| local mentioned_files = context.context.mentioned_files or {}\n00099| local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n00100| \n00101| if not allowed then\n00102| vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n00103| return\n00104| end\n00105| \n00106| opts = opts or {}\n00107| \n00108| opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n00109| state.current_context_config = opts.context\n00110| context.load()\n00111| opts.model = opts.model or state.current_model\n00112| opts.agent = opts.agent or state.current_mode or config.default_mode\n00113| \n00114| local params = {}\n00115| \n00116| if opts.model then\n00117| local provider, model = opts.model:match('^(.-)/(.+)$')\n00118| params.model = { providerID = provider, modelID = model }\n00119| state.current_model = opts.model\n00120| end\n00121| \n00122| if opts.agent then\n00123| params.agent = opts.agent\n00124| state.current_mode = opts.agent\n00125| end\n00126| \n00127| params.parts = context.format_message(prompt, opts.context)\n00128| M.before_run(opts)\n00129| \n00130| state.api_client\n00131| :create_message(state.active_session.id, params)\n00132| :and_then(function(response)\n00133| if not response or not response.info or not response.parts then\n00134| -- fall back to full render. incremental render is handled\n00135| -- event manager\n00136| ui.render_output()\n00137| end\n00138| \n00139| M.after_run(prompt)\n00140| end)\n00141| :catch(function(err)\n00142| vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n00143| M.cancel()\n00144| end)\n00145| end\n00146| \n00147| ---@param title? string\n00148| ---@return Session?\n00149| function M.create_new_session(title)\n00150| local session_response = state.api_client\n00151| :create_session(title and { title = title } or false)\n00152| :catch(function(err)\n00153| vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n00154| end)\n00155| :wait()\n00156| \n00157| if session_response and session_response.id then\n00158| local new_session = session.get_by_id(session_response.id)\n00159| return new_session\n00160| end\n00161| end\n00162| \n00163| ---@param prompt string\n00164| function M.after_run(prompt)\n00165| context.unload_attachments()\n00166| state.last_sent_context = vim.deepcopy(context.context)\n00167| require('opencode.history').write(prompt)\n00168| M._abort_count = 0\n00169| end\n00170| \n00171| ---@param opts? SendMessageOpts\n00172| function M.before_run(opts)\n00173| local is_new_session = opts and opts.new_session or not state.active_session\n00174| opts = opts or {}\n00175| \n00176| M.cancel()\n00177| -- ui.clear_output()\n00178| \n00179| M.open({\n00180| new_session = is_new_session,\n00181| })\n00182| end\n00183| \n00184| function M.configure_provider()\n00185| require('opencode.provider').select(function(selection)\n00186| if not selection then\n00187| if state.windows then\n00188| ui.focus_input()\n00189| end\n00190| return\n00191| end\n00192| local model_str = string.format('%s/%s', selection.provider, selection.model)\n00193| state.current_model = model_str\n00194| \n00195| if state.windows then\n00196| require('opencode.ui.topbar').render()\n00197| ui.focus_input()\n00198| else\n00199| vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n00200| end\n00201| end)\n00202| end\n00203| \n00204| function M.cancel()\n00205| if state.windows and state.active_session then\n00206| if state.is_running() then\n00207| M._abort_count = M._abort_count + 1\n00208| \n00209| -- if there's a current permission, reject it\n00210| if state.current_permission then\n00211| require('opencode.api').permission_deny()\n00212| end\n00213| \n00214| local ok, result = pcall(function()\n00215| return state.api_client:abort_session(state.active_session.id):wait()\n00216| end)\n00217| \n00218| if not ok then\n00219| vim.notify('Abort error: ' .. vim.inspect(result))\n00220| end\n00221| \n00222| if M._abort_count >= 3 then\n00223| vim.notify('Re-starting Opencode server')\n00224| M._abort_count = 0\n00225| -- close existing server\n00226| if state.opencode_server then\n00227| state.opencode_server:shutdown():wait()\n00228| end\n00229| \n00230| -- start a new one\n00231| state.opencode_server = nil\n00232| \n00233| -- NOTE: start a new server here to make sure we're subscribed\n00234| -- to server events before a user sends a message\n00235| state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n00236| end\n00237| end\n00238| require('opencode.ui.footer').clear()\n00239| input_window.set_content('')\n00240| require('opencode.history').index = nil\n00241| ui.focus_input()\n00242| end\n00243| end\n00244| \n00245| function M.opencode_ok()\n00246| if vim.fn.executable('opencode') == 0 then\n00247| vim.notify(\n00248| 'opencode command not found - please install and configure opencode before using this plugin',\n00249| vim.log.levels.ERROR\n00250| )\n00251| return false\n00252| end\n00253| \n00254| if not state.opencode_cli_version or state.opencode_cli_version == '' then\n00255| local result = vim.system({ 'opencode', '--version' }):wait()\n00256| local out = (result and result.stdout or ''):gsub('%s+$', '')\n00257| state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n00258| end\n00259| \n00260| local required = state.required_version\n00261| local current_version = state.opencode_cli_version\n00262| \n00263| if not current_version or current_version == '' then\n00264| vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n00265| return false\n00266| end\n00267| \n00268| if not util.is_version_greater_or_equal(current_version, required) then\n00269| vim.notify(\n00270| string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n00271| vim.log.levels.ERROR\n00272| )\n00273| return false\n00274| end\n00275| \n00276| return true\n00277| end\n00278| \n00279| local function on_opencode_server()\n00280| state.current_permission = nil\n00281| end\n00282| \n00283| --- Switches the current mode to the specified agent.\n00284| --- @param mode string The agent/mode to switch to\n00285| --- @return boolean success Returns true if the mode was switched successfully, false otherwise\n00286| function M.switch_to_mode(mode)\n00287| if not mode or mode == '' then\n00288| vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n00289| return false\n00290| end\n00291| \n00292| local config_file = require('opencode.config_file')\n00293| local available_agents = config_file.get_opencode_agents()\n00294| \n00295| if not vim.tbl_contains(available_agents, mode) then\n00296| vim.notify(\n00297| string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n00298| vim.log.levels.ERROR\n00299| )\n00300| return false\n00301| end\n00302| \n00303| state.current_mode = mode\n00304| ui.render_output()\n00305| return true\n00306| end\n00307| \n00308| --- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n00309| --- @return boolean success Returns true if current_mode is set\n00310| function M.ensure_current_mode()\n00311| if state.current_mode == nil then\n00312| local config_file = require('opencode.config_file')\n00313| local available_agents = config_file.get_opencode_agents()\n00314| \n00315| if not available_agents or #available_agents == 0 then\n00316| vim.notify('No available agents found', vim.log.levels.ERROR)\n00317| return false\n00318| end\n00319| \n00320| local default_mode = config.default_mode\n00321| \n00322| -- Try to use the configured default mode if it's available\n00323| if default_mode and vim.tbl_contains(available_agents, default_mode) then\n00324| state.current_mode = default_mode\n00325| else\n00326| -- Fallback to first available agent\n00327| state.current_mode = available_agents[1]\n00328| end\n00329| end\n00330| return true\n00331| end\n00332| \n00333| function M.setup()\n00334| state.subscribe('opencode_server', on_opencode_server)\n00335| \n00336| vim.schedule(function()\n00337| M.opencode_ok()\n00338| end)\n00339| local OpencodeApiClient = require('opencode.api_client')\n00340| state.api_client = OpencodeApiClient.create()\n00341| end\n00342| \n00343| return M\n00344| \n","id":"prt_a49ed91db002IGcvEBKqHjLjcH","synthetic":true,"type":"text","messageID":"msg_a49ed91d6001coTsjFq9x6FF5W","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","id":"prt_a49ed91db00317OxenmCorS0sB","messageID":"msg_a49ed91d6001coTsjFq9x6FF5W","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","mime":"text/plain","type":"file","filename":"lua/opencode/core.lua"}}},{"type":"message.part.updated","properties":{"part":{"text":"{\"content\":[{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"param-type-not-match\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":20},\"end\":{\"line\":130,\"character\":43}},\"message\":\"expected `string` but found `string?`. \",\"source\":\"EmmyLua\",\"code\":\"param-type-not-match\"}},\"namespace\":103,\"_extmark_id\":1,\"end_col\":43,\"message\":\"expected `string` but found `string?`. \",\"source\":\"EmmyLua\",\"col\":20},{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"param-type-not-match\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":45},\"end\":{\"line\":130,\"character\":51}},\"message\":\"expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`. member model not match, expect { modelID: string, providerID: string }?, but got { providerID = string?, modelID = string? }\",\"source\":\"EmmyLua\",\"code\":\"param-type-not-match\"}},\"namespace\":103,\"_extmark_id\":2,\"end_col\":51,\"message\":\"expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`. member model not match, expect { modelID: string, providerID: string }?, but got { providerID = string?, modelID = string? }\",\"source\":\"EmmyLua\",\"col\":45},{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"need-check-nil\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":20},\"end\":{\"line\":130,\"character\":40}},\"message\":\"state.active_session may be nil\",\"source\":\"EmmyLua\",\"code\":\"need-check-nil\"}},\"namespace\":103,\"_extmark_id\":3,\"end_col\":40,\"message\":\"state.active_session may be nil\",\"source\":\"EmmyLua\",\"col\":20}],\"context_type\":\"diagnostics\"}","id":"prt_a49ed91d70021vvuvYgF7OgY5D","synthetic":true,"type":"text","messageID":"msg_a49ed91d6001coTsjFq9x6FF5W","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176897510},"directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2025-11-03T13:34:32.514Z","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176899112},"id":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":0,"write":0},"input":0,"output":0},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"id":"msg_a49ed91d6001coTsjFq9x6FF5W","time":{"created":1762176897494},"summary":{"diffs":[]},"role":"user","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176899140},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2025-11-03T13:34:32.514Z","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_a49ed9b590016OgTFP0GBP3nPs","snapshot":"8e7903714919009004aad8754db0035fb47ecb24","type":"step-start","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"130"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176900011},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Fixing param-type-not-match and need-check-nil in core.lua line 130","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"-match"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" **"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130,","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":","}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"nil"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" be"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"string"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" nullable"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session`","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"`"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"-not"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID:","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":":"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" tools"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" parts"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts =","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" ="}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"`\n"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" ("}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string?","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"?"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string,","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":","}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, provider","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" provider"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"ity"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" matches"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `{"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"-check"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" **"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" check"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" nil"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"---\n\n"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] ","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" "}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `."}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" expected"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, provider","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" provider"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" diagnostics"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" resolved"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\n","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"`\n\n"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session`","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"`"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176899931},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"`."}},{"type":"message.part.updated","properties":{"part":{"callID":"call_ABJ98MrScaXDP17UCnRACu6s","id":"prt_a49edb4c4001QGmKwYoe4BcZ9j","tool":"edit","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","state":{"status":"running","input":{"newString":" if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)","filePath":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","oldString":" state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)"},"time":{"start":1762176906475}},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"file.edited","properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"}},{"type":"message.part.updated","properties":{"part":{"text":"Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.","id":"prt_a49ed9b5b001nHdvHRhJEv3ZRU","time":{"start":1762176906487,"end":1762176906487},"type":"text","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"callID":"call_ABJ98MrScaXDP17UCnRACu6s","id":"prt_a49edb4c4001QGmKwYoe4BcZ9j","tool":"edit","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","state":{"metadata":{"filediff":{"file":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":0,"additions":4,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n+++ /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n@@ -126,8 +126,12 @@\n \n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n \n+if not state.active_session or not state.active_session.id then\n+ vim.notify('No active session to send message to', vim.log.levels.ERROR)\n+ return\n+end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n","diagnostics":{"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/autocmds.lua":[{"severity":2,"range":{"start":{"line":11,"character":2},"end":{"line":11,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":2},"end":{"line":37,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":2},"end":{"line":48,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":2},"end":{"line":58,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":16},"end":{"line":5,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":23},"end":{"line":47,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":9},"end":{"line":16,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":8},"end":{"line":17,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":44},"end":{"line":32,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":31},"end":{"line":33,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":23},"end":{"line":66,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":24},"end":{"line":62,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion.lua":[{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":59},"end":{"line":35,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":6},"end":{"line":90,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":6},"end":{"line":93,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/commands.lua":[{"severity":2,"range":{"start":{"line":81,"character":8},"end":{"line":81,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":37},"end":{"line":27,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":10},"end":{"line":75,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":56},"end":{"line":81,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config_file.lua":[{"severity":2,"range":{"start":{"line":17,"character":4},"end":{"line":17,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":4},"end":{"line":34,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":14},"end":{"line":61,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":11},"end":{"line":90,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":53},"end":{"line":17,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":74},"end":{"line":17,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":54},"end":{"line":34,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":75},"end":{"line":34,"character":78}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":56},"end":{"line":51,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":77},"end":{"line":51,"character":80}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/blink_cmp.lua":[{"severity":2,"range":{"start":{"line":24,"character":9},"end":{"line":24,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":37},"end":{"line":35,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":49},"end":{"line":35,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/nvim_cmp.lua":[{"severity":2,"range":{"start":{"line":83,"character":18},"end":{"line":83,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":11},"end":{"line":26,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":14},"end":{"line":86,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":39},"end":{"line":34,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":51},"end":{"line":34,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/vim_complete.lua":[{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":2},"end":{"line":25,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":116,"character":25},"end":{"line":116,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":21},"end":{"line":20,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":15},"end":{"line":46,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":14},"end":{"line":47,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":15},"end":{"line":64,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":14},"end":{"line":65,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":37},"end":{"line":38,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":9},"end":{"line":85,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":49},"end":{"line":38,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":6},"end":{"line":108,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":6},"end":{"line":9,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":6},"end":{"line":10,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":41},"end":{"line":106,"character":44}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua":[{"severity":2,"range":{"start":{"line":286,"character":2},"end":{"line":286,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":290,"character":2},"end":{"line":290,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":2},"end":{"line":292,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":297,"character":2},"end":{"line":297,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":296,"character":2},"end":{"line":296,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":2},"end":{"line":298,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":2},"end":{"line":299,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":504,"character":2},"end":{"line":504,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":516,"character":2},"end":{"line":516,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":21},"end":{"line":58,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":27},"end":{"line":59,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":23},"end":{"line":73,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":22},"end":{"line":78,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":26},"end":{"line":79,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":4},"end":{"line":144,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":18},"end":{"line":218,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":25},"end":{"line":266,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":479,"character":18},"end":{"line":479,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":45},"end":{"line":28,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":29},"end":{"line":51,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":22},"end":{"line":92,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":115,"character":29},"end":{"line":115,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":29},"end":{"line":129,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":18},"end":{"line":141,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":142,"character":17},"end":{"line":142,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":9},"end":{"line":148,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":9},"end":{"line":150,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":9},"end":{"line":156,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":167,"character":29},"end":{"line":167,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":9},"end":{"line":179,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":182,"character":29},"end":{"line":182,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":196,"character":29},"end":{"line":196,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":8},"end":{"line":237,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":15},"end":{"line":249,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":21},"end":{"line":265,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":22},"end":{"line":281,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":282,"character":18},"end":{"line":282,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":22},"end":{"line":283,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":15},"end":{"line":287,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":21},"end":{"line":291,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":293,"character":19},"end":{"line":293,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":24},"end":{"line":298,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":312,"character":19},"end":{"line":312,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":479,"character":51},"end":{"line":479,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":483,"character":11},"end":{"line":483,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":492,"character":11},"end":{"line":492,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":503,"character":18},"end":{"line":503,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":11},"end":{"line":255,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":256,"character":16},"end":{"line":256,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":334,"character":11},"end":{"line":334,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":347,"character":11},"end":{"line":347,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":357,"character":11},"end":{"line":357,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":423,"character":27},"end":{"line":423,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":60},"end":{"line":30,"character":63}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":34},"end":{"line":83,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":34},"end":{"line":86,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":34},"end":{"line":89,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":31},"end":{"line":250,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":42},"end":{"line":354,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":9},"end":{"line":276,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":34},"end":{"line":266,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":509,"character":49},"end":{"line":509,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":521,"character":49},"end":{"line":521,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":38},"end":{"line":141,"character":42}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":64},"end":{"line":298,"character":68}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":70},"end":{"line":298,"character":75}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":77},"end":{"line":298,"character":81}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":250,"character":51},"end":{"line":250,"character":55}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/sort.lua":[{"severity":2,"range":{"start":{"line":31,"character":40},"end":{"line":31,"character":43}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":40},"end":{"line":32,"character":43}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/context_bar.lua":[{"severity":2,"range":{"start":{"line":172,"character":2},"end":{"line":172,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":2},"end":{"line":162,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":16},"end":{"line":149,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":10},"end":{"line":151,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":18},"end":{"line":148,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":9},"end":{"line":155,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":4},"end":{"line":180,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":7},"end":{"line":98,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":7},"end":{"line":99,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":7},"end":{"line":100,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":20},"end":{"line":175,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/contextual_actions.lua":[{"severity":2,"range":{"start":{"line":22,"character":2},"end":{"line":22,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":2},"end":{"line":42,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":4},"end":{"line":82,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":16},"end":{"line":19,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":18},"end":{"line":20,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":6},"end":{"line":88,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":6},"end":{"line":26,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":6},"end":{"line":46,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":6},"end":{"line":63,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":8},"end":{"line":31,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":8},"end":{"line":54,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":10},"end":{"line":90,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":25},"end":{"line":27,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":25},"end":{"line":50,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":93,"character":27},"end":{"line":93,"character":33}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/debug_helper.lua":[{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":2},"end":{"line":77,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":2},"end":{"line":16,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":2},"end":{"line":76,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":19},"end":{"line":16,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":4},"end":{"line":20,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":4},"end":{"line":32,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":4},"end":{"line":64,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":4},"end":{"line":70,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":16},"end":{"line":75,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":18},"end":{"line":11,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":19},"end":{"line":12,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":23},"end":{"line":35,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":51},"end":{"line":46,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":19},"end":{"line":74,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":72},"end":{"line":77,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":5},"end":{"line":18,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":46},"end":{"line":32,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":48},"end":{"line":64,"character":51}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":45},"end":{"line":70,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/diff_tab.lua":[{"severity":2,"range":{"start":{"line":37,"character":2},"end":{"line":37,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":2},"end":{"line":56,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":24},"end":{"line":35,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":4},"end":{"line":43,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":4},"end":{"line":44,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":10},"end":{"line":14,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":27},"end":{"line":36,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":17},"end":{"line":38,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":18},"end":{"line":54,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":22},"end":{"line":55,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":22},"end":{"line":5,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":10},"end":{"line":6,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":6},"end":{"line":47,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":6},"end":{"line":48,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":20},"end":{"line":7,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":18},"end":{"line":10,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":9},"end":{"line":16,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":50},"end":{"line":35,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":23},"end":{"line":40,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":25},"end":{"line":44,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":23},"end":{"line":51,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":16},"end":{"line":25,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":29},"end":{"line":18,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":13},"end":{"line":19,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/file_picker.lua":[{"severity":2,"range":{"start":{"line":157,"character":2},"end":{"line":157,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":21},"end":{"line":103,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":22},"end":{"line":104,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":21},"end":{"line":105,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":11},"end":{"line":11,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":6},"end":{"line":130,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":6},"end":{"line":134,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":8},"end":{"line":132,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":26},"end":{"line":126,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua":[{"severity":2,"range":{"start":{"line":400,"character":43},"end":{"line":400,"character":57}},"message":"Undefined type or alias `vim.Diagnostic`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":160,"character":73},"end":{"line":160,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":37},"end":{"line":224,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":310,"character":16},"end":{"line":310,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":326,"character":19},"end":{"line":326,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":367,"character":19},"end":{"line":367,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":384,"character":19},"end":{"line":384,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":438,"character":5},"end":{"line":438,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":448,"character":19},"end":{"line":448,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":16},"end":{"line":563,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":698,"character":16},"end":{"line":698,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":437,"character":14},"end":{"line":437,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":6},"end":{"line":21,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":64},"end":{"line":283,"character":67}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":475,"character":27},"end":{"line":475,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":484,"character":30},"end":{"line":484,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":497,"character":27},"end":{"line":497,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":26},"end":{"line":563,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":700,"character":12},"end":{"line":700,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":798,"character":64},"end":{"line":798,"character":67}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":300,"character":10},"end":{"line":300,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":571,"character":19},"end":{"line":571,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":304,"character":34},"end":{"line":304,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":413,"character":17},"end":{"line":413,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":655,"character":23},"end":{"line":655,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":781,"character":42},"end":{"line":781,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":68},"end":{"line":299,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":769,"character":38},"end":{"line":769,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":49,"character":71},"end":{"line":49,"character":77}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/highlight.lua":[{"severity":2,"range":{"start":{"line":3,"character":19},"end":{"line":3,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":4},"end":{"line":6,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":4},"end":{"line":7,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":4},"end":{"line":8,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":4},"end":{"line":9,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":4},"end":{"line":10,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":4},"end":{"line":15,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":4},"end":{"line":16,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":4},"end":{"line":17,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":4},"end":{"line":20,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":4},"end":{"line":21,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":4},"end":{"line":22,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":4},"end":{"line":23,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":4},"end":{"line":25,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":4},"end":{"line":26,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":4},"end":{"line":27,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":4},"end":{"line":29,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":4},"end":{"line":30,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":4},"end":{"line":32,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":4},"end":{"line":36,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":4},"end":{"line":37,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":4},"end":{"line":38,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":4},"end":{"line":43,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":4},"end":{"line":44,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":4},"end":{"line":46,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":4},"end":{"line":47,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":4},"end":{"line":48,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":4},"end":{"line":49,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":4},"end":{"line":50,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":4},"end":{"line":53,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":4},"end":{"line":54,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":4},"end":{"line":55,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":4},"end":{"line":58,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":4},"end":{"line":59,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":4},"end":{"line":60,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":4},"end":{"line":61,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/icons.lua":[{"severity":2,"range":{"start":{"line":89,"character":6},"end":{"line":89,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":8},"end":{"line":90,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":10},"end":{"line":92,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/loading_animation.lua":[{"severity":2,"range":{"start":{"line":28,"character":11},"end":{"line":28,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":10},"end":{"line":13,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":4},"end":{"line":100,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":28},"end":{"line":45,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":9},"end":{"line":34,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":62},"end":{"line":34,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/mention.lua":[{"severity":2,"range":{"start":{"line":103,"character":2},"end":{"line":103,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":27},"end":{"line":4,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":2},"end":{"line":104,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":2},"end":{"line":105,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":8},"end":{"line":11,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":6},"end":{"line":33,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":23},"end":{"line":94,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":4},"end":{"line":112,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":32},"end":{"line":14,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":26},"end":{"line":56,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":7},"end":{"line":132,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":7},"end":{"line":137,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":116,"character":25},"end":{"line":116,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":27},"end":{"line":137,"character":31}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/navigation.lua":[{"severity":2,"range":{"start":{"line":53,"character":2},"end":{"line":53,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":2},"end":{"line":86,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":23},"end":{"line":32,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":19},"end":{"line":34,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":21},"end":{"line":52,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":23},"end":{"line":66,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":19},"end":{"line":68,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":6},"end":{"line":47,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":6},"end":{"line":82,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output.lua":[{"severity":2,"range":{"start":{"line":94,"character":9},"end":{"line":94,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":9},"end":{"line":110,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":30},"end":{"line":141,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":30},"end":{"line":145,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output_window.lua":[{"severity":2,"range":{"start":{"line":4,"character":14},"end":{"line":4,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":2},"end":{"line":39,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":2},"end":{"line":40,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":2},"end":{"line":42,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":2},"end":{"line":43,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":2},"end":{"line":44,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":2},"end":{"line":45,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":2},"end":{"line":47,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":2},"end":{"line":48,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":2},"end":{"line":49,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":2},"end":{"line":50,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":2},"end":{"line":51,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":2},"end":{"line":61,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":2},"end":{"line":89,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":2},"end":{"line":90,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":2},"end":{"line":91,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":2},"end":{"line":106,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":2},"end":{"line":141,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":158,"character":2},"end":{"line":158,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":2},"end":{"line":168,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":9},"end":{"line":69,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":4},"end":{"line":139,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":8},"end":{"line":148,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":8},"end":{"line":149,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":21},"end":{"line":7,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":22},"end":{"line":58,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":12},"end":{"line":132,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":11},"end":{"line":30,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":6},"end":{"line":162,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":172,"character":6},"end":{"line":172,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua":[{"severity":2,"range":{"start":{"line":656,"character":2},"end":{"line":656,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":733,"character":2},"end":{"line":733,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":4},"end":{"line":507,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":685,"character":4},"end":{"line":685,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":699,"character":4},"end":{"line":699,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":194,"character":18},"end":{"line":194,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":17},"end":{"line":195,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":21},"end":{"line":197,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":4},"end":{"line":213,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":669,"character":9},"end":{"line":669,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":31},"end":{"line":189,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":453,"character":30},"end":{"line":453,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":54},"end":{"line":507,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":73},"end":{"line":507,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":685,"character":36},"end":{"line":685,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":316,"character":23},"end":{"line":316,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":9},"end":{"line":30,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":9},"end":{"line":32,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/session_picker.lua":[{"severity":2,"range":{"start":{"line":423,"character":2},"end":{"line":423,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":16},"end":{"line":233,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":25},"end":{"line":53,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":365,"character":6},"end":{"line":365,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":396,"character":6},"end":{"line":396,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":401,"character":6},"end":{"line":401,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":8},"end":{"line":342,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":10},"end":{"line":324,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":8},"end":{"line":402,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":22},"end":{"line":249,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":51},"end":{"line":402,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":69},"end":{"line":402,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":22},"end":{"line":54,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":253,"character":20},"end":{"line":253,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":20},"end":{"line":276,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":343,"character":22},"end":{"line":343,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":24},"end":{"line":107,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/timer.lua":[{"severity":2,"range":{"start":{"line":31,"character":18},"end":{"line":31,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":16},"end":{"line":25,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":53,"character":15},"end":{"line":53,"character":21}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":32,"character":45},"end":{"line":32,"character":51}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/helpers.lua":[{"severity":2,"range":{"start":{"line":112,"character":2},"end":{"line":112,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":2},"end":{"line":64,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":2},"end":{"line":78,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":2},"end":{"line":98,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":109,"character":26},"end":{"line":109,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":25},"end":{"line":96,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":9},"end":{"line":65,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":8},"end":{"line":91,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":4},"end":{"line":102,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":157,"character":9},"end":{"line":157,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":10},"end":{"line":237,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":21},"end":{"line":249,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":19},"end":{"line":45,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":6},"end":{"line":122,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":264,"character":14},"end":{"line":264,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":20},"end":{"line":39,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":15},"end":{"line":70,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":71,"character":10},"end":{"line":71,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":7},"end":{"line":86,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":25},"end":{"line":84,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":87,"character":12},"end":{"line":87,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":12},"end":{"line":262,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":263,"character":15},"end":{"line":263,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":177,"character":28},"end":{"line":177,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":17},"end":{"line":180,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":39},"end":{"line":197,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":36},"end":{"line":199,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/manual/init_replay.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":0},"end":{"line":21,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":0},"end":{"line":22,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":0},"end":{"line":23,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":0},"end":{"line":27,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":0},"end":{"line":19,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":0},"end":{"line":24,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":0,"character":20},"end":{"line":0,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":2},"end":{"line":16,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":3},"end":{"line":8,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":5},"end":{"line":10,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/manual/renderer_replay.lua":[{"severity":2,"range":{"start":{"line":35,"character":2},"end":{"line":35,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":2},"end":{"line":134,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":2},"end":{"line":200,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":2},"end":{"line":205,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":2},"end":{"line":265,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":2},"end":{"line":266,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":284,"character":2},"end":{"line":284,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":289,"character":2},"end":{"line":289,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":293,"character":2},"end":{"line":293,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":2},"end":{"line":298,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":303,"character":2},"end":{"line":303,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":2},"end":{"line":307,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":311,"character":2},"end":{"line":311,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":315,"character":2},"end":{"line":315,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":2},"end":{"line":319,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":331,"character":2},"end":{"line":331,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":336,"character":2},"end":{"line":336,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":337,"character":2},"end":{"line":337,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":338,"character":2},"end":{"line":338,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":339,"character":2},"end":{"line":339,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":340,"character":2},"end":{"line":340,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":341,"character":2},"end":{"line":341,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":4},"end":{"line":112,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":152,"character":4},"end":{"line":152,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":4},"end":{"line":181,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":4},"end":{"line":189,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":4},"end":{"line":262,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":4},"end":{"line":356,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":20},"end":{"line":16,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":69},"end":{"line":35,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":6},"end":{"line":70,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":21},"end":{"line":134,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":16},"end":{"line":162,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":163,"character":19},"end":{"line":163,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":14},"end":{"line":168,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":57},"end":{"line":200,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":207,"character":4},"end":{"line":207,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":246,"character":4},"end":{"line":246,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":14},"end":{"line":255,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":256,"character":15},"end":{"line":256,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":257,"character":21},"end":{"line":257,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":4},"end":{"line":333,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":27},"end":{"line":26,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":176,"character":6},"end":{"line":176,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":47},"end":{"line":19,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":61},"end":{"line":28,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":152,"character":45},"end":{"line":152,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":173,"character":17},"end":{"line":173,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":49},"end":{"line":181,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":59},"end":{"line":189,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":6},"end":{"line":213,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":54},"end":{"line":258,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":325,"character":6},"end":{"line":325,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":79},"end":{"line":356,"character":82}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":6},"end":{"line":48,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":45},"end":{"line":70,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":18},"end":{"line":222,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":223,"character":21},"end":{"line":223,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":81},"end":{"line":333,"character":84}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":12},"end":{"line":54,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":12},"end":{"line":55,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":176,"character":66},"end":{"line":176,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":208,"character":13},"end":{"line":208,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":8},"end":{"line":51,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":8},"end":{"line":52,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":325,"character":61},"end":{"line":325,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/init.lua":[{"severity":2,"range":{"start":{"line":32,"character":0},"end":{"line":32,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":0},"end":{"line":9,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":0},"end":{"line":26,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":27},"end":{"line":4,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":24},"end":{"line":5,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":20},"end":{"line":8,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":3},"end":{"line":15,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":5},"end":{"line":17,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":2},"end":{"line":62,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":2},"end":{"line":74,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":2},"end":{"line":80,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":4},"end":{"line":63,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":4},"end":{"line":64,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":24},"end":{"line":13,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":22},"end":{"line":17,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":4},"end":{"line":65,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":26},"end":{"line":27,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua":[{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":2},"end":{"line":4,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":2},"end":{"line":15,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":2},"end":{"line":60,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua":[{"severity":2,"range":{"start":{"line":7,"character":0},"end":{"line":7,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":2},"end":{"line":26,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":2},"end":{"line":114,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":2},"end":{"line":144,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":4},"end":{"line":66,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":4},"end":{"line":86,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":115,"character":4},"end":{"line":115,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":4},"end":{"line":155,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":165,"character":4},"end":{"line":165,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":4},"end":{"line":175,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":4},"end":{"line":187,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":198,"character":4},"end":{"line":198,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":4},"end":{"line":205,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":9},"end":{"line":12,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":6},"end":{"line":90,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":22},"end":{"line":94,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":31},"end":{"line":199,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":66},"end":{"line":201,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":2},"end":{"line":18,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":2},"end":{"line":51,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":2},"end":{"line":66,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":2},"end":{"line":81,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":4},"end":{"line":10,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":24},"end":{"line":9,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":16},"end":{"line":61,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":16},"end":{"line":62,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":16},"end":{"line":63,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":16},"end":{"line":76,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":17},"end":{"line":77,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":16},"end":{"line":78,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":23},"end":{"line":35,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":52,"character":23},"end":{"line":59,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":67,"character":23},"end":{"line":74,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":83,"character":23},"end":{"line":90,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":2},"end":{"line":26,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":22},"end":{"line":11,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":20},"end":{"line":13,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua":[{"severity":2,"range":{"start":{"line":6,"character":0},"end":{"line":6,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":2},"end":{"line":32,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":109,"character":2},"end":{"line":109,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":2},"end":{"line":232,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":4},"end":{"line":21,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":4},"end":{"line":65,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":4},"end":{"line":81,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":4},"end":{"line":90,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":4},"end":{"line":106,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":4},"end":{"line":110,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":4},"end":{"line":121,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":4},"end":{"line":137,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":157,"character":4},"end":{"line":157,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":177,"character":4},"end":{"line":177,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":198,"character":4},"end":{"line":198,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":211,"character":4},"end":{"line":211,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":223,"character":4},"end":{"line":223,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":4},"end":{"line":233,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":24},"end":{"line":37,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":32},"end":{"line":41,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":18},"end":{"line":42,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":4},"end":{"line":69,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":4},"end":{"line":73,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":4},"end":{"line":77,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":4},"end":{"line":102,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":4},"end":{"line":103,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":4},"end":{"line":104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":32},"end":{"line":38,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":36},"end":{"line":39,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":36},"end":{"line":40,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":6},"end":{"line":212,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/util.lua":[{"severity":2,"range":{"start":{"line":61,"character":9},"end":{"line":61,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":18},"end":{"line":15,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":16},"end":{"line":27,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":16},"end":{"line":281,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":414,"character":9},"end":{"line":414,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":419,"character":9},"end":{"line":419,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":450,"character":24},"end":{"line":450,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":19},"end":{"line":14,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":19},"end":{"line":16,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":253,"character":25},"end":{"line":253,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":404,"character":20},"end":{"line":404,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":12},"end":{"line":237,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":28},"end":{"line":322,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":16},"end":{"line":247,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":21},"end":{"line":319,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":343,"character":20},"end":{"line":343,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":203,"character":27},"end":{"line":203,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":37},"end":{"line":255,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":31},"end":{"line":258,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":41},"end":{"line":319,"character":49}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":282,"character":7},"end":{"line":282,"character":13}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":238,"character":11},"end":{"line":238,"character":17}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/state.lua":[{"severity":2,"range":{"start":{"line":133,"character":2},"end":{"line":133,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":161,"character":14},"end":{"line":161,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":174,"character":14},"end":{"line":174,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":11},"end":{"line":195,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":10},"end":{"line":138,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":10},"end":{"line":187,"character":12}},"message":"Missing required fields in type `OpencodeState`: `input_content`, `is_opencode_focused`, `current_mode`, `last_output`, `restore_points`, `cost`, `tokens_count`, `job_count`, `api_client`, `required_version`, `append`, `remove`, `subscribe`, `subscribe`, `unsubscribe`, `is_running`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua":[{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":2},"end":{"line":114,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":4},"end":{"line":9,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":4},"end":{"line":68,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":117,"character":4},"end":{"line":117,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":22},"end":{"line":6,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":4},"end":{"line":108,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":4},"end":{"line":156,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":6},"end":{"line":26,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/session.lua":[{"severity":2,"range":{"start":{"line":54,"character":9},"end":{"line":54,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":4},"end":{"line":50,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":24},"end":{"line":114,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":15},"end":{"line":20,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":15},"end":{"line":28,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":15},"end":{"line":36,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":15},"end":{"line":94,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":135,"character":24},"end":{"line":135,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":33},"end":{"line":145,"character":36}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":69},"end":{"line":11,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":51},"end":{"line":50,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":72},"end":{"line":50,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":72,"character":17},"end":{"line":72,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":31},"end":{"line":144,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":178,"character":50},"end":{"line":178,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":31},"end":{"line":95,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":46},"end":{"line":95,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/context.lua":[{"severity":2,"range":{"start":{"line":206,"character":2},"end":{"line":206,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":2},"end":{"line":224,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":246,"character":4},"end":{"line":246,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":4},"end":{"line":247,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":4},"end":{"line":248,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":288,"character":4},"end":{"line":288,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":24},"end":{"line":74,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":18},"end":{"line":206,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":209,"character":15},"end":{"line":209,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":4},"end":{"line":218,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":18},"end":{"line":224,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":12},"end":{"line":50,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":12},"end":{"line":53,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":12},"end":{"line":56,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":24},"end":{"line":265,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":270,"character":7},"end":{"line":270,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":208,"character":24},"end":{"line":208,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":23},"end":{"line":106,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":251,"character":14},"end":{"line":251,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":26},"end":{"line":267,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":59},"end":{"line":96,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":37},"end":{"line":218,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":18},"end":{"line":206,"character":76}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":78},"end":{"line":206,"character":81}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":18},"end":{"line":224,"character":72}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":74},"end":{"line":224,"character":77}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":58},"end":{"line":206,"character":62}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":64},"end":{"line":206,"character":69}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":71},"end":{"line":206,"character":75}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":54},"end":{"line":224,"character":58}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":60},"end":{"line":224,"character":65}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":67},"end":{"line":224,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":208,"character":17},"end":{"line":208,"character":23}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/input_window.lua":[{"severity":2,"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":2},"end":{"line":54,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":2},"end":{"line":55,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":2},"end":{"line":95,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":97,"character":2},"end":{"line":97,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":2},"end":{"line":98,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":2},"end":{"line":100,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":2},"end":{"line":101,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":2},"end":{"line":104,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":2},"end":{"line":105,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":2},"end":{"line":106,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":2},"end":{"line":126,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":2},"end":{"line":170,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":2},"end":{"line":179,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":2},"end":{"line":197,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":2},"end":{"line":206,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":225,"character":2},"end":{"line":225,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":2},"end":{"line":245,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":2},"end":{"line":255,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":8},"end":{"line":44,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":8},"end":{"line":45,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":16},"end":{"line":81,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":22},"end":{"line":83,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":4},"end":{"line":90,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":50},"end":{"line":195,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":20},"end":{"line":5,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":22},"end":{"line":22,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":22},"end":{"line":121,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":23},"end":{"line":122,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":146,"character":4},"end":{"line":146,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":4},"end":{"line":162,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":54},"end":{"line":170,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":16},"end":{"line":181,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":215,"character":16},"end":{"line":215,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":20},"end":{"line":218,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":235,"character":16},"end":{"line":235,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":19},"end":{"line":88,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":11},"end":{"line":31,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":37},"end":{"line":53,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":43},"end":{"line":90,"character":46}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":135,"character":18},"end":{"line":135,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":18},"end":{"line":138,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":22},"end":{"line":139,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":56},"end":{"line":162,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":26},"end":{"line":258,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/ui.lua":[{"severity":2,"range":{"start":{"line":234,"character":2},"end":{"line":234,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":2},"end":{"line":68,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":2},"end":{"line":73,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":2},"end":{"line":74,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":117,"character":2},"end":{"line":117,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":2},"end":{"line":136,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":8},"end":{"line":22,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":8},"end":{"line":23,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":8},"end":{"line":25,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":8},"end":{"line":26,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":8},"end":{"line":27,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":8},"end":{"line":28,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":4},"end":{"line":54,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":9},"end":{"line":58,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":2},"end":{"line":80,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":22},"end":{"line":147,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":16},"end":{"line":164,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":4},"end":{"line":191,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":22},"end":{"line":218,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":18},"end":{"line":38,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":120,"character":10},"end":{"line":120,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":10},"end":{"line":139,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":42},"end":{"line":85,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":29},"end":{"line":86,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":5},"end":{"line":122,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":124,"character":6},"end":{"line":124,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":123,"character":7},"end":{"line":123,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/git_review.lua":[{"severity":2,"range":{"start":{"line":10,"character":16},"end":{"line":10,"character":30}},"message":"Undefined type or alias `vim.SystemOpts`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":19,"character":2},"end":{"line":19,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":2},"end":{"line":107,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":2},"end":{"line":356,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":4},"end":{"line":15,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":11},"end":{"line":22,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":20},"end":{"line":106,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":2},"end":{"line":292,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":17},"end":{"line":20,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":2},"end":{"line":260,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":4},"end":{"line":128,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":4},"end":{"line":141,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":6},"end":{"line":224,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":6},"end":{"line":233,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":234,"character":6},"end":{"line":234,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":4},"end":{"line":249,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":261,"character":4},"end":{"line":261,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":4},"end":{"line":283,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":4},"end":{"line":299,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":318,"character":4},"end":{"line":318,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":373,"character":6},"end":{"line":373,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":375,"character":6},"end":{"line":375,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":8},"end":{"line":68,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":8},"end":{"line":74,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":8},"end":{"line":80,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":4},"end":{"line":149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":25},"end":{"line":212,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":21},"end":{"line":213,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":310,"character":4},"end":{"line":310,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":49},"end":{"line":78,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":6},"end":{"line":150,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":172,"character":6},"end":{"line":172,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":190,"character":6},"end":{"line":190,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":6},"end":{"line":255,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":329,"character":4},"end":{"line":329,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":271,"character":8},"end":{"line":271,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":305,"character":6},"end":{"line":305,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":6},"end":{"line":324,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":330,"character":6},"end":{"line":330,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":373,"character":72},"end":{"line":373,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":375,"character":76},"end":{"line":375,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":29},"end":{"line":40,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":228,"character":7},"end":{"line":228,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":5},"end":{"line":287,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":13},"end":{"line":262,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":340,"character":10},"end":{"line":340,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":15},"end":{"line":151,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":331,"character":15},"end":{"line":331,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/server_job.lua":[{"severity":2,"range":{"start":{"line":100,"character":12},"end":{"line":100,"character":15}},"message":"Undefined type or alias `Job`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":11,"character":35},"end":{"line":11,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":6},"end":{"line":114,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":8},"end":{"line":56,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":25},"end":{"line":64,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":8},"end":{"line":118,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":124,"character":25},"end":{"line":124,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":51},"end":{"line":114,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":69},"end":{"line":114,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":10},"end":{"line":57,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":76},"end":{"line":118,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":12},"end":{"line":39,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":12},"end":{"line":46,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":62},"end":{"line":57,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":14},"end":{"line":40,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":14},"end":{"line":47,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":72},"end":{"line":40,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":66},"end":{"line":47,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua":[{"severity":2,"range":{"start":{"line":21,"character":0},"end":{"line":21,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":2},"end":{"line":92,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":2},"end":{"line":100,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":111,"character":2},"end":{"line":111,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":2},"end":{"line":242,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":421,"character":2},"end":{"line":421,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":486,"character":2},"end":{"line":486,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":4},"end":{"line":114,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":4},"end":{"line":119,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":4},"end":{"line":129,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":4},"end":{"line":162,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":4},"end":{"line":180,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":4},"end":{"line":199,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":4},"end":{"line":212,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":4},"end":{"line":245,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":274,"character":4},"end":{"line":274,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":280,"character":4},"end":{"line":280,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":4},"end":{"line":287,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":306,"character":4},"end":{"line":306,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":338,"character":4},"end":{"line":338,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":370,"character":4},"end":{"line":370,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":396,"character":4},"end":{"line":396,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":422,"character":4},"end":{"line":422,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":446,"character":4},"end":{"line":446,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":470,"character":4},"end":{"line":470,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":487,"character":4},"end":{"line":487,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":509,"character":4},"end":{"line":509,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":6},"end":{"line":249,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":6},"end":{"line":258,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":269,"character":6},"end":{"line":269,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":6},"end":{"line":250,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":251,"character":6},"end":{"line":251,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":259,"character":6},"end":{"line":259,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":6},"end":{"line":262,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":6},"end":{"line":265,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":15},"end":{"line":249,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":16},"end":{"line":258,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":173,"character":30},"end":{"line":173,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":188,"character":29},"end":{"line":188,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":526,"character":11},"end":{"line":526,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":0},"end":{"line":40,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":0},"end":{"line":50,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":0},"end":{"line":94,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":111,"character":0},"end":{"line":111,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":2},"end":{"line":45,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":2},"end":{"line":52,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":2},"end":{"line":69,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":2},"end":{"line":75,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":2},"end":{"line":95,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":2},"end":{"line":112,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":2},"end":{"line":118,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":125,"character":2},"end":{"line":125,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":2},"end":{"line":132,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":2},"end":{"line":136,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":4},"end":{"line":119,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":4},"end":{"line":126,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":97,"character":35},"end":{"line":97,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":42},"end":{"line":104,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":54},"end":{"line":105,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":17},"end":{"line":12,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua":[{"severity":3,"range":{"start":{"line":246,"character":8},"end":{"line":246,"character":21}},"message":"Global variable in lowercase initial, Did you miss `local` or misspell it?","source":"Lua Diagnostics.","code":"lowercase-global"},{"severity":2,"range":{"start":{"line":30,"character":0},"end":{"line":30,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":2},"end":{"line":36,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":2},"end":{"line":130,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":174,"character":2},"end":{"line":174,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":2},"end":{"line":199,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":263,"character":2},"end":{"line":263,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":4},"end":{"line":103,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":131,"character":4},"end":{"line":131,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":4},"end":{"line":144,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":4},"end":{"line":151,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":4},"end":{"line":175,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":4},"end":{"line":200,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":221,"character":4},"end":{"line":221,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":240,"character":4},"end":{"line":240,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":278,"character":4},"end":{"line":278,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":284,"character":4},"end":{"line":284,"character":14}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":290,"character":4},"end":{"line":290,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":297,"character":4},"end":{"line":297,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":4},"end":{"line":307,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":317,"character":4},"end":{"line":317,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":22},"end":{"line":38,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":24},"end":{"line":40,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":4},"end":{"line":104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":26},"end":{"line":39,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":21},"end":{"line":37,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":285,"character":6},"end":{"line":285,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":301,"character":6},"end":{"line":301,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":311,"character":6},"end":{"line":311,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":321,"character":6},"end":{"line":321,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":214,"character":6},"end":{"line":214,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":279,"character":24},"end":{"line":279,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":286,"character":6},"end":{"line":286,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":6},"end":{"line":291,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":6},"end":{"line":298,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":308,"character":6},"end":{"line":308,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":318,"character":6},"end":{"line":318,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":280,"character":28},"end":{"line":280,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":21},"end":{"line":27,"character":3}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":88,"character":28},"end":{"line":94,"character":5}},"message":"Missing required fields in type `OpencodeServer`: `spawn_promise`, `shutdown_promise`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":146,"character":29},"end":{"line":146,"character":51}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":202,"character":29},"end":{"line":202,"character":45}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":243,"character":29},"end":{"line":243,"character":45}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":254,"character":20},"end":{"line":254,"character":58}},"message":"Missing required fields in type `OpencodeContextConfig`: `enabled`, `cursor_data`, `diagnostics`, `selection`, `agents`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/curl.lua":[{"severity":2,"range":{"start":{"line":39,"character":16},"end":{"line":39,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":4},"end":{"line":134,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":16},"end":{"line":81,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":15},"end":{"line":126,"character":16}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":126,"character":21},"end":{"line":126,"character":30}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api_client.lua":[{"severity":2,"range":{"start":{"line":382,"character":12},"end":{"line":382,"character":15}},"message":"Undefined type or alias `Job`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":223,"character":164},"end":{"line":223,"character":168}},"message":"Undefined type or alias `Part`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":393,"character":28},"end":{"line":393,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":393,"character":45},"end":{"line":393,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua":[{"severity":2,"data":"syntax","range":{"start":{"line":148,"character":118},"end":{"line":148,"character":118}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":149,"character":121},"end":{"line":149,"character":121}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":150,"character":108},"end":{"line":150,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":151,"character":108},"end":{"line":151,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":152,"character":117},"end":{"line":152,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":153,"character":117},"end":{"line":153,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":154,"character":112},"end":{"line":154,"character":112}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":155,"character":102},"end":{"line":155,"character":102}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":156,"character":108},"end":{"line":156,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":157,"character":108},"end":{"line":157,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":158,"character":104},"end":{"line":158,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":159,"character":114},"end":{"line":159,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":160,"character":114},"end":{"line":160,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":161,"character":100},"end":{"line":161,"character":100}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":162,"character":117},"end":{"line":162,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":163,"character":110},"end":{"line":163,"character":110}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":164,"character":104},"end":{"line":164,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":165,"character":115},"end":{"line":165,"character":115}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":166,"character":109},"end":{"line":166,"character":109}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":167,"character":113},"end":{"line":167,"character":113}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":168,"character":126},"end":{"line":168,"character":126}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":181,"character":118},"end":{"line":181,"character":118}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":182,"character":121},"end":{"line":182,"character":121}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":183,"character":108},"end":{"line":183,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":184,"character":108},"end":{"line":184,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":185,"character":117},"end":{"line":185,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":186,"character":117},"end":{"line":186,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":187,"character":112},"end":{"line":187,"character":112}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":188,"character":102},"end":{"line":188,"character":102}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":189,"character":108},"end":{"line":189,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":190,"character":108},"end":{"line":190,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":191,"character":104},"end":{"line":191,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":192,"character":114},"end":{"line":192,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":193,"character":114},"end":{"line":193,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":194,"character":100},"end":{"line":194,"character":100}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":195,"character":117},"end":{"line":195,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":196,"character":110},"end":{"line":196,"character":110}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":197,"character":104},"end":{"line":197,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":198,"character":115},"end":{"line":198,"character":115}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":199,"character":109},"end":{"line":199,"character":109}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":200,"character":113},"end":{"line":200,"character":113}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":201,"character":126},"end":{"line":201,"character":126}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"range":{"start":{"line":281,"character":39},"end":{"line":281,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":308,"character":10},"end":{"line":308,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":361,"character":8},"end":{"line":361,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":2},"end":{"line":15,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":2},"end":{"line":64,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":2},"end":{"line":77,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":2},"end":{"line":93,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":113,"character":2},"end":{"line":113,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":2},"end":{"line":14,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":2},"end":{"line":25,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":2},"end":{"line":36,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":2},"end":{"line":49,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":2},"end":{"line":60,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":19},"end":{"line":9,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":19},"end":{"line":10,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":19},"end":{"line":20,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":19},"end":{"line":21,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua":[{"severity":2,"range":{"start":{"line":339,"character":2},"end":{"line":339,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":28},"end":{"line":16,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":17},"end":{"line":107,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":4},"end":{"line":130,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":28},"end":{"line":169,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":4},"end":{"line":250,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":4},"end":{"line":267,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":272,"character":4},"end":{"line":272,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":4},"end":{"line":291,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":4},"end":{"line":299,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":6},"end":{"line":145,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":9},"end":{"line":298,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":6},"end":{"line":61,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":5},"end":{"line":249,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":6},"end":{"line":319,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":59},"end":{"line":101,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":55},"end":{"line":130,"character":58}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":6},"end":{"line":202,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":6},"end":{"line":252,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":97},"end":{"line":267,"character":100}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":274,"character":6},"end":{"line":274,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":39},"end":{"line":291,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":301,"character":6},"end":{"line":301,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":326,"character":24},"end":{"line":326,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":57},"end":{"line":145,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":75},"end":{"line":145,"character":78}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":8},"end":{"line":222,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":8},"end":{"line":226,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":19},"end":{"line":258,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":70},"end":{"line":61,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":6},"end":{"line":156,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":46},"end":{"line":319,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":62},"end":{"line":202,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":38},"end":{"line":222,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":51},"end":{"line":156,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":69},"end":{"line":156,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":59},"end":{"line":136,"character":64}},"message":"Undefined field `parts`.","source":"Lua Diagnostics.","code":"undefined-field"},{"severity":2,"range":{"start":{"line":136,"character":38},"end":{"line":136,"character":42}},"message":"Undefined field `info`.","source":"Lua Diagnostics.","code":"undefined-field"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/health.lua":[{"severity":2,"range":{"start":{"line":2,"character":15},"end":{"line":2,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":9},"end":{"line":6,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":9},"end":{"line":114,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":14},"end":{"line":154,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":17},"end":{"line":14,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":5},"end":{"line":148,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":18},"end":{"line":147,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/history.lua":[{"severity":2,"range":{"start":{"line":9,"character":29},"end":{"line":9,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/id.lua":[{"severity":2,"range":{"start":{"line":139,"character":9},"end":{"line":139,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":9},"end":{"line":101,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":11},"end":{"line":119,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":39},"end":{"line":66,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/keymap.lua":[{"severity":2,"range":{"start":{"line":38,"character":9},"end":{"line":38,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":9},"end":{"line":49,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":19},"end":{"line":15,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":8},"end":{"line":21,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":12},"end":{"line":74,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":12},"end":{"line":75,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":8},"end":{"line":19,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":8},"end":{"line":65,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":98},"end":{"line":21,"character":101}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/opencode_server.lua":[{"severity":2,"range":{"start":{"line":58,"character":34},"end":{"line":58,"character":53}},"message":"Undefined type or alias `vim.SystemCompleted`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":13},"end":{"line":66,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":12},"end":{"line":18,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/provider.lua":[{"severity":2,"range":{"start":{"line":22,"character":2},"end":{"line":22,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/promise.lua":[{"severity":2,"range":{"start":{"line":53,"character":10},"end":{"line":53,"character":15}},"message":"Undefined param `error`.","source":"Lua Diagnostics.","code":"undefined-doc-param"},{"severity":2,"range":{"start":{"line":5,"character":77},"end":{"line":5,"character":78}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":2,"character":37},"end":{"line":2,"character":38}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":5,"character":34},"end":{"line":5,"character":35}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":2,"character":62},"end":{"line":2,"character":63}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":42,"character":24},"end":{"line":42,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":25},"end":{"line":62,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":178,"character":18},"end":{"line":178,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":26},"end":{"line":103,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":27},"end":{"line":150,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua":[{"severity":2,"range":{"start":{"line":6,"character":0},"end":{"line":6,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":2},"end":{"line":38,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":2},"end":{"line":44,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":2},"end":{"line":242,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":359,"character":2},"end":{"line":359,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":425,"character":2},"end":{"line":425,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":4},"end":{"line":79,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":4},"end":{"line":121,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":243,"character":4},"end":{"line":243,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":4},"end":{"line":276,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":309,"character":4},"end":{"line":309,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":4},"end":{"line":342,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":360,"character":4},"end":{"line":360,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":393,"character":4},"end":{"line":393,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":426,"character":4},"end":{"line":426,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":465,"character":4},"end":{"line":465,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":496,"character":4},"end":{"line":496,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":23},"end":{"line":21,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":26},"end":{"line":20,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":6},"end":{"line":281,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":306,"character":6},"end":{"line":306,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":365,"character":6},"end":{"line":365,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":389,"character":6},"end":{"line":389,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":278,"character":30},"end":{"line":278,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":362,"character":30},"end":{"line":362,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":390,"character":6},"end":{"line":390,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":421,"character":6},"end":{"line":421,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":461,"character":6},"end":{"line":461,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":493,"character":6},"end":{"line":493,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":521,"character":6},"end":{"line":521,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":456,"character":21},"end":{"line":456,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":457,"character":21},"end":{"line":457,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":458,"character":21},"end":{"line":458,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":295,"character":19},"end":{"line":295,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":369,"character":20},"end":{"line":369,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":378,"character":19},"end":{"line":378,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":394,"character":20},"end":{"line":394,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":430,"character":20},"end":{"line":430,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":469,"character":20},"end":{"line":469,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":500,"character":20},"end":{"line":500,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":428,"character":33},"end":{"line":428,"character":48}},"message":"Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":467,"character":33},"end":{"line":467,"character":48}},"message":"Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/plugin/opencode.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1,"character":3},"end":{"line":1,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/topbar.lua":[{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":2},"end":{"line":79,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":16},"end":{"line":65,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":10},"end":{"line":68,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":18},"end":{"line":64,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":72,"character":9},"end":{"line":72,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":4},"end":{"line":107,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":86},"end":{"line":108,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/review.lua":[{"severity":2,"range":{"start":{"line":71,"character":2},"end":{"line":71,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":2},"end":{"line":140,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":192,"character":2},"end":{"line":192,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":182,"character":2},"end":{"line":182,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":146,"character":4},"end":{"line":146,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":4},"end":{"line":147,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":4},"end":{"line":149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":4},"end":{"line":150,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":14},"end":{"line":48,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":19},"end":{"line":49,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":87,"character":4},"end":{"line":87,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":10},"end":{"line":88,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":17},"end":{"line":110,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":17},"end":{"line":141,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":18},"end":{"line":180,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":22},"end":{"line":181,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":20},"end":{"line":191,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":2},"end":{"line":307,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":22},"end":{"line":76,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":10},"end":{"line":77,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":6},"end":{"line":175,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":19},"end":{"line":19,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":26},"end":{"line":63,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":18},"end":{"line":64,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":6},"end":{"line":66,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":20},"end":{"line":79,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":4},"end":{"line":202,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":4},"end":{"line":292,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":4},"end":{"line":324,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":4},"end":{"line":333,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":8},"end":{"line":38,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":18},"end":{"line":50,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":18},"end":{"line":83,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":9},"end":{"line":91,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":158,"character":8},"end":{"line":158,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":8},"end":{"line":162,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":163,"character":8},"end":{"line":163,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":8},"end":{"line":164,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":167,"character":8},"end":{"line":167,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":8},"end":{"line":168,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":171,"character":8},"end":{"line":171,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":4},"end":{"line":210,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":312,"character":23},"end":{"line":312,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":313,"character":19},"end":{"line":313,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":31},"end":{"line":57,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":24},"end":{"line":155,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":159,"character":26},"end":{"line":159,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":18},"end":{"line":210,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":6},"end":{"line":224,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":239,"character":6},"end":{"line":239,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":29},"end":{"line":13,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":16},"end":{"line":101,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":296,"character":5},"end":{"line":296,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":328,"character":5},"end":{"line":328,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":29},"end":{"line":93,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":13},"end":{"line":94,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/snapshot.lua":[{"severity":2,"range":{"start":{"line":8,"character":16},"end":{"line":8,"character":30}},"message":"Undefined type or alias `vim.SystemOpts`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":217,"character":2},"end":{"line":217,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":236,"character":2},"end":{"line":236,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":2},"end":{"line":252,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":9},"end":{"line":61,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":9},"end":{"line":164,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":194,"character":4},"end":{"line":194,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":4},"end":{"line":30,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":4},"end":{"line":46,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":4},"end":{"line":70,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":4},"end":{"line":91,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":4},"end":{"line":122,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":4},"end":{"line":128,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":133,"character":4},"end":{"line":133,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":4},"end":{"line":154,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":160,"character":4},"end":{"line":160,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":4},"end":{"line":210,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":4},"end":{"line":226,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":4},"end":{"line":232,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":4},"end":{"line":242,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":4},"end":{"line":248,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":259,"character":19},"end":{"line":259,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":11},"end":{"line":20,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":20},"end":{"line":27,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":4},"end":{"line":85,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":14},"end":{"line":138,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":20},"end":{"line":140,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":24},"end":{"line":168,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":171,"character":20},"end":{"line":171,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":6},"end":{"line":187,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":24},"end":{"line":205,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":214,"character":4},"end":{"line":214,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":236,"character":51},"end":{"line":236,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":82},"end":{"line":252,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":17},"end":{"line":18,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":5},"end":{"line":84,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":24},"end":{"line":89,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":6},"end":{"line":191,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":36},"end":{"line":40,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":51},"end":{"line":52,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":80},"end":{"line":57,"character":83}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":36},"end":{"line":70,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":57},"end":{"line":91,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":36},"end":{"line":122,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":56},"end":{"line":128,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":133,"character":72},"end":{"line":133,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":36},"end":{"line":154,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":160,"character":67},"end":{"line":160,"character":70}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":61},"end":{"line":179,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":6},"end":{"line":212,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":78},"end":{"line":226,"character":81}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":82},"end":{"line":232,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":78},"end":{"line":242,"character":81}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":82},"end":{"line":248,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":8},"end":{"line":189,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":44},"end":{"line":89,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":184,"character":44},"end":{"line":184,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/files.lua":[{"severity":2,"range":{"start":{"line":75,"character":2},"end":{"line":75,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":5},"end":{"line":32,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":83},"end":{"line":75,"character":86}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":19},"end":{"line":82,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":14},"end":{"line":83,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":20},"end":{"line":86,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":15},"end":{"line":94,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":11},"end":{"line":136,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":7},"end":{"line":169,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":27},"end":{"line":18,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":18},"end":{"line":62,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":18},"end":{"line":129,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":24},"end":{"line":168,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":16},"end":{"line":19,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":13},"end":{"line":28,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":14},"end":{"line":33,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":13},"end":{"line":36,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":184,"character":31},"end":{"line":184,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":22},"end":{"line":169,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":54},"end":{"line":169,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":27},"end":{"line":170,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":131,"character":13},"end":{"line":131,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api.lua":[{"severity":3,"range":{"start":{"line":650,"character":9},"end":{"line":650,"character":35}},"message":"Global variable in lowercase initial, Did you miss `local` or misspell it?","source":"Lua Diagnostics.","code":"lowercase-global"},{"severity":2,"range":{"start":{"line":207,"character":2},"end":{"line":207,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":2},"end":{"line":245,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":2},"end":{"line":260,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":385,"character":2},"end":{"line":385,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1175,"character":2},"end":{"line":1175,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":71,"character":9},"end":{"line":71,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":9},"end":{"line":78,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":4},"end":{"line":201,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":4},"end":{"line":322,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":332,"character":4},"end":{"line":332,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":4},"end":{"line":342,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":4},"end":{"line":354,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":358,"character":4},"end":{"line":358,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":363,"character":4},"end":{"line":363,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":461,"character":26},"end":{"line":461,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":522,"character":4},"end":{"line":522,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":542,"character":4},"end":{"line":542,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":548,"character":4},"end":{"line":548,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":553,"character":4},"end":{"line":553,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":575,"character":4},"end":{"line":575,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":600,"character":4},"end":{"line":600,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":621,"character":4},"end":{"line":621,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":627,"character":4},"end":{"line":627,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":690,"character":4},"end":{"line":690,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":695,"character":4},"end":{"line":695,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":740,"character":4},"end":{"line":740,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1091,"character":15},"end":{"line":1091,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1104,"character":4},"end":{"line":1104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1109,"character":16},"end":{"line":1109,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1149,"character":4},"end":{"line":1149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":207,"character":65},"end":{"line":207,"character":68}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":239,"character":23},"end":{"line":239,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":24},"end":{"line":245,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":254,"character":23},"end":{"line":254,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":38},"end":{"line":255,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":24},"end":{"line":260,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":531,"character":6},"end":{"line":531,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":567,"character":6},"end":{"line":567,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":592,"character":6},"end":{"line":592,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":612,"character":6},"end":{"line":612,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":641,"character":6},"end":{"line":641,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":747,"character":6},"end":{"line":747,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1115,"character":11},"end":{"line":1115,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1128,"character":11},"end":{"line":1128,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1134,"character":11},"end":{"line":1134,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1102,"character":18},"end":{"line":1102,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1113,"character":24},"end":{"line":1113,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1150,"character":6},"end":{"line":1150,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1154,"character":6},"end":{"line":1154,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":61},"end":{"line":179,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":61},"end":{"line":201,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":57},"end":{"line":322,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":332,"character":57},"end":{"line":332,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":57},"end":{"line":342,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":47},"end":{"line":354,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":358,"character":36},"end":{"line":358,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":363,"character":74},"end":{"line":363,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":522,"character":36},"end":{"line":522,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":542,"character":47},"end":{"line":542,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":548,"character":36},"end":{"line":548,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":553,"character":68},"end":{"line":553,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":562,"character":6},"end":{"line":562,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":575,"character":45},"end":{"line":575,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":582,"character":6},"end":{"line":582,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":600,"character":47},"end":{"line":600,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":607,"character":6},"end":{"line":607,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":621,"character":44},"end":{"line":621,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":627,"character":42},"end":{"line":627,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":636,"character":6},"end":{"line":636,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":690,"character":44},"end":{"line":690,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":695,"character":34},"end":{"line":695,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":713,"character":8},"end":{"line":713,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":729,"character":8},"end":{"line":729,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":740,"character":50},"end":{"line":740,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1104,"character":53},"end":{"line":1104,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":8},"end":{"line":568,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":8},"end":{"line":593,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":8},"end":{"line":613,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":8},"end":{"line":642,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":8},"end":{"line":748,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":776,"character":8},"end":{"line":776,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":845,"character":8},"end":{"line":845,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":875,"character":8},"end":{"line":875,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":909,"character":8},"end":{"line":909,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":922,"character":8},"end":{"line":922,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":931,"character":8},"end":{"line":931,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":954,"character":8},"end":{"line":954,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":969,"character":8},"end":{"line":969,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":981,"character":8},"end":{"line":981,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":993,"character":8},"end":{"line":993,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":996,"character":31},"end":{"line":996,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1023,"character":8},"end":{"line":1023,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":708,"character":8},"end":{"line":708,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":724,"character":8},"end":{"line":724,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1116,"character":13},"end":{"line":1116,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1129,"character":13},"end":{"line":1129,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1135,"character":13},"end":{"line":1135,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1152,"character":8},"end":{"line":1152,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":459,"character":38},"end":{"line":459,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":8},"end":{"line":563,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":608,"character":8},"end":{"line":608,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":637,"character":8},"end":{"line":637,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":10},"end":{"line":714,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":10},"end":{"line":730,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":896,"character":10},"end":{"line":896,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":906,"character":10},"end":{"line":906,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":52},"end":{"line":568,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":70},"end":{"line":568,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":50},"end":{"line":593,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":68},"end":{"line":593,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":52},"end":{"line":613,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":70},"end":{"line":613,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":54},"end":{"line":642,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":72},"end":{"line":642,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":56},"end":{"line":748,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":74},"end":{"line":748,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":776,"character":59},"end":{"line":776,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":845,"character":73},"end":{"line":845,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":875,"character":70},"end":{"line":875,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":909,"character":61},"end":{"line":909,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":922,"character":43},"end":{"line":922,"character":46}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":931,"character":62},"end":{"line":931,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":954,"character":71},"end":{"line":954,"character":74}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":969,"character":38},"end":{"line":969,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":981,"character":38},"end":{"line":981,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":993,"character":44},"end":{"line":993,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1023,"character":76},"end":{"line":1023,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":585,"character":10},"end":{"line":585,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":587,"character":10},"end":{"line":587,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":709,"character":10},"end":{"line":709,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":725,"character":10},"end":{"line":725,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":819,"character":35},"end":{"line":819,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":823,"character":12},"end":{"line":823,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":53},"end":{"line":563,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":584,"character":10},"end":{"line":584,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":608,"character":52},"end":{"line":608,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":51},"end":{"line":714,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":69},"end":{"line":714,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":51},"end":{"line":730,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":69},"end":{"line":730,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":896,"character":86},"end":{"line":896,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":906,"character":86},"end":{"line":906,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":585,"character":94},"end":{"line":585,"character":97}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":587,"character":60},"end":{"line":587,"character":63}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":823,"character":55},"end":{"line":823,"character":58}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":60},"end":{"line":245,"character":64}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":245,"character":66},"end":{"line":245,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":245,"character":73},"end":{"line":245,"character":77}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":60},"end":{"line":260,"character":64}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":66},"end":{"line":260,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":73},"end":{"line":260,"character":77}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/throttling_emitter.lua":[{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":4},"end":{"line":53,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/types.lua":[{"severity":2,"range":{"start":{"line":246,"character":28},"end":{"line":246,"character":54}},"message":"Undefined type or alias `vim.api.keyset.set_extmark`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":320,"character":24},"end":{"line":320,"character":38}},"message":"Undefined type or alias `vim.Diagnostic`.","source":"Lua Diagnostics.","code":"undefined-doc-name"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config.lua":[{"severity":2,"range":{"start":{"line":178,"character":11},"end":{"line":178,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":4},"end":{"line":226,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":13},"end":{"line":247,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":9},"end":{"line":200,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":289,"character":31},"end":{"line":289,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":98},"end":{"line":226,"character":101}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":305,"character":11},"end":{"line":305,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/footer.lua":[{"severity":2,"range":{"start":{"line":85,"character":2},"end":{"line":85,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":2},"end":{"line":122,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":2},"end":{"line":129,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":2},"end":{"line":140,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":2},"end":{"line":154,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":2},"end":{"line":155,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":2},"end":{"line":156,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":20},"end":{"line":49,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":23},"end":{"line":84,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":8},"end":{"line":114,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":21},"end":{"line":128,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":21},"end":{"line":139,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":12},"end":{"line":62,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":10},"end":{"line":98,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":10},"end":{"line":99,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":113,"character":8},"end":{"line":113,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":10},"end":{"line":64,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}]}},"status":"completed","time":{"start":1762176906475,"end":1762176906837},"title":"lua/opencode/core.lua","input":{"newString":" if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)","filePath":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","oldString":" state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)"},"output":"\nThis file has errors, please fix\n\n\n\n"},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"reason":"tool-calls","cost":0,"id":"prt_a49edb65e001xdiFvgkXFJjIiw","messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","snapshot":"f33f38a70b284207e092c2c578a24e96fdd1bd4d","type":"step-finish","tokens":{"reasoning":0,"cache":{"read":10752,"write":0},"input":14938,"output":576},"sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176899112},"id":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":10752,"write":0},"input":14938,"output":576},"providerID":"github-copilot"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"files":["/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"],"messageID":"msg_a49ed9828001mN6CDcWPnGnpHS","id":"prt_a49edb681001xxBeFUSvyhS2Eo","type":"patch","hash":"8e7903714919009004aad8754db0035fb47ecb24","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176899112,"completed":1762176906882},"id":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":10752,"write":0},"input":14938,"output":576},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176899112,"completed":1762176906890},"id":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":10752,"write":0},"input":14938,"output":576},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176899112,"completed":1762176906891},"id":"msg_a49ed9828001mN6CDcWPnGnpHS","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":10752,"write":0},"input":14938,"output":576},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176906900},"id":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":0,"write":0},"input":0,"output":0},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"id":"msg_a49ed91d6001coTsjFq9x6FF5W","time":{"created":1762176897494},"summary":{"diffs":[{"file":"lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":0,"additions":4,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"}]},"role":"user","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176906932},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Fixing param-type-not-match and need-check-nil in core.lua line 130","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_a49edb95a001BXEIud4AFAjA3v","snapshot":"f33f38a70b284207e092c2c578a24e96fdd1bd4d","type":"step-start","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"id"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" step"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" the"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" string"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"\n"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":")/"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, model","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" model"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"end"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" string"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `model","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"model"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" to"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" now"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now.","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176907612},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"."}},{"type":"message.part.updated","properties":{"part":{"callID":"call_Kwbm6aS5sRCah2asJyUHTyZu","id":"prt_a49edc06d001Em2jkf8QmWbwv3","tool":"edit","messageID":"msg_a49edb694001syExFxMUW1ik3n","state":{"status":"pending"},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"callID":"call_Kwbm6aS5sRCah2asJyUHTyZu","id":"prt_a49edc06d001Em2jkf8QmWbwv3","tool":"edit","messageID":"msg_a49edb694001syExFxMUW1ik3n","state":{"status":"running","input":{"newString":" params.model = { modelID = model, providerID = provider }","filePath":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","oldString":" params.model = { providerID = provider, modelID = model }"},"time":{"start":1762176909474}},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"file.edited","properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"}},{"type":"message.part.updated","properties":{"part":{"text":"Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now.","id":"prt_a49edb95c0019lorS0C7cytxrl","time":{"start":1762176909485,"end":1762176909485},"type":"text","messageID":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"callID":"call_Kwbm6aS5sRCah2asJyUHTyZu","id":"prt_a49edc06d001Em2jkf8QmWbwv3","tool":"edit","messageID":"msg_a49edb694001syExFxMUW1ik3n","state":{"metadata":{"filediff":{"file":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":1,"additions":1,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n+++ /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n@@ -114,9 +114,9 @@\n local params = {}\n \n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n- params.model = { providerID = provider, modelID = model }\n+ params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n \n if opts.agent then\n","diagnostics":{"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/autocmds.lua":[{"severity":2,"range":{"start":{"line":11,"character":2},"end":{"line":11,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":2},"end":{"line":37,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":2},"end":{"line":48,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":2},"end":{"line":58,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":16},"end":{"line":5,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":23},"end":{"line":47,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":9},"end":{"line":16,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":8},"end":{"line":17,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":44},"end":{"line":32,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":31},"end":{"line":33,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":23},"end":{"line":66,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":24},"end":{"line":62,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion.lua":[{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":59},"end":{"line":35,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":6},"end":{"line":90,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":6},"end":{"line":93,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/commands.lua":[{"severity":2,"range":{"start":{"line":81,"character":8},"end":{"line":81,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":37},"end":{"line":27,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":10},"end":{"line":75,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":56},"end":{"line":81,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config_file.lua":[{"severity":2,"range":{"start":{"line":17,"character":4},"end":{"line":17,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":4},"end":{"line":34,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":14},"end":{"line":61,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":11},"end":{"line":90,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":53},"end":{"line":17,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":74},"end":{"line":17,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":54},"end":{"line":34,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":75},"end":{"line":34,"character":78}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":56},"end":{"line":51,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":77},"end":{"line":51,"character":80}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/blink_cmp.lua":[{"severity":2,"range":{"start":{"line":24,"character":9},"end":{"line":24,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":37},"end":{"line":35,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":49},"end":{"line":35,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/nvim_cmp.lua":[{"severity":2,"range":{"start":{"line":83,"character":18},"end":{"line":83,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":11},"end":{"line":26,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":14},"end":{"line":86,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":39},"end":{"line":34,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":51},"end":{"line":34,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/vim_complete.lua":[{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":2},"end":{"line":25,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":116,"character":25},"end":{"line":116,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":21},"end":{"line":20,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":15},"end":{"line":46,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":14},"end":{"line":47,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":15},"end":{"line":64,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":14},"end":{"line":65,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":37},"end":{"line":38,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":9},"end":{"line":85,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":49},"end":{"line":38,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":6},"end":{"line":108,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":6},"end":{"line":9,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":6},"end":{"line":10,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":41},"end":{"line":106,"character":44}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua":[{"severity":2,"range":{"start":{"line":286,"character":2},"end":{"line":286,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":290,"character":2},"end":{"line":290,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":2},"end":{"line":292,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":297,"character":2},"end":{"line":297,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":296,"character":2},"end":{"line":296,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":2},"end":{"line":298,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":2},"end":{"line":299,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":504,"character":2},"end":{"line":504,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":516,"character":2},"end":{"line":516,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":21},"end":{"line":58,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":27},"end":{"line":59,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":23},"end":{"line":73,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":22},"end":{"line":78,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":26},"end":{"line":79,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":4},"end":{"line":144,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":18},"end":{"line":218,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":25},"end":{"line":266,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":479,"character":18},"end":{"line":479,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":45},"end":{"line":28,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":29},"end":{"line":51,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":22},"end":{"line":92,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":115,"character":29},"end":{"line":115,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":29},"end":{"line":129,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":18},"end":{"line":141,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":142,"character":17},"end":{"line":142,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":9},"end":{"line":148,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":9},"end":{"line":150,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":9},"end":{"line":156,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":167,"character":29},"end":{"line":167,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":9},"end":{"line":179,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":182,"character":29},"end":{"line":182,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":196,"character":29},"end":{"line":196,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":8},"end":{"line":237,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":15},"end":{"line":249,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":21},"end":{"line":265,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":22},"end":{"line":281,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":282,"character":18},"end":{"line":282,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":22},"end":{"line":283,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":15},"end":{"line":287,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":21},"end":{"line":291,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":293,"character":19},"end":{"line":293,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":24},"end":{"line":298,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":312,"character":19},"end":{"line":312,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":479,"character":51},"end":{"line":479,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":483,"character":11},"end":{"line":483,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":492,"character":11},"end":{"line":492,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":503,"character":18},"end":{"line":503,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":11},"end":{"line":255,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":256,"character":16},"end":{"line":256,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":334,"character":11},"end":{"line":334,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":347,"character":11},"end":{"line":347,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":357,"character":11},"end":{"line":357,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":423,"character":27},"end":{"line":423,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":60},"end":{"line":30,"character":63}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":34},"end":{"line":83,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":34},"end":{"line":86,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":34},"end":{"line":89,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":31},"end":{"line":250,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":42},"end":{"line":354,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":9},"end":{"line":276,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":34},"end":{"line":266,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":509,"character":49},"end":{"line":509,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":521,"character":49},"end":{"line":521,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":38},"end":{"line":141,"character":42}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":64},"end":{"line":298,"character":68}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":70},"end":{"line":298,"character":75}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":298,"character":77},"end":{"line":298,"character":81}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":250,"character":51},"end":{"line":250,"character":55}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/sort.lua":[{"severity":2,"range":{"start":{"line":31,"character":40},"end":{"line":31,"character":43}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":40},"end":{"line":32,"character":43}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/context_bar.lua":[{"severity":2,"range":{"start":{"line":172,"character":2},"end":{"line":172,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":2},"end":{"line":162,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":16},"end":{"line":149,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":10},"end":{"line":151,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":18},"end":{"line":148,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":9},"end":{"line":155,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":4},"end":{"line":180,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":7},"end":{"line":98,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":7},"end":{"line":99,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":7},"end":{"line":100,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":20},"end":{"line":175,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/contextual_actions.lua":[{"severity":2,"range":{"start":{"line":22,"character":2},"end":{"line":22,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":2},"end":{"line":42,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":4},"end":{"line":82,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":16},"end":{"line":19,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":18},"end":{"line":20,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":6},"end":{"line":88,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":6},"end":{"line":26,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":6},"end":{"line":46,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":6},"end":{"line":63,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":8},"end":{"line":31,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":8},"end":{"line":54,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":10},"end":{"line":90,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":25},"end":{"line":27,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":25},"end":{"line":50,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":93,"character":27},"end":{"line":93,"character":33}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/debug_helper.lua":[{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":2},"end":{"line":77,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":2},"end":{"line":16,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":2},"end":{"line":76,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":19},"end":{"line":16,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":4},"end":{"line":20,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":4},"end":{"line":32,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":4},"end":{"line":64,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":4},"end":{"line":70,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":16},"end":{"line":75,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":18},"end":{"line":11,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":19},"end":{"line":12,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":23},"end":{"line":35,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":51},"end":{"line":46,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":19},"end":{"line":74,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":72},"end":{"line":77,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":5},"end":{"line":18,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":46},"end":{"line":32,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":48},"end":{"line":64,"character":51}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":45},"end":{"line":70,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/diff_tab.lua":[{"severity":2,"range":{"start":{"line":37,"character":2},"end":{"line":37,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":2},"end":{"line":56,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":24},"end":{"line":35,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":4},"end":{"line":43,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":4},"end":{"line":44,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":10},"end":{"line":14,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":27},"end":{"line":36,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":17},"end":{"line":38,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":18},"end":{"line":54,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":22},"end":{"line":55,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":22},"end":{"line":5,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":10},"end":{"line":6,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":6},"end":{"line":47,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":6},"end":{"line":48,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":20},"end":{"line":7,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":18},"end":{"line":10,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":9},"end":{"line":16,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":50},"end":{"line":35,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":23},"end":{"line":40,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":25},"end":{"line":44,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":23},"end":{"line":51,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":16},"end":{"line":25,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":29},"end":{"line":18,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":13},"end":{"line":19,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/file_picker.lua":[{"severity":2,"range":{"start":{"line":157,"character":2},"end":{"line":157,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":21},"end":{"line":103,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":22},"end":{"line":104,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":21},"end":{"line":105,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":11},"end":{"line":11,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":6},"end":{"line":130,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":6},"end":{"line":134,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":8},"end":{"line":132,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":26},"end":{"line":126,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua":[{"severity":2,"range":{"start":{"line":400,"character":43},"end":{"line":400,"character":57}},"message":"Undefined type or alias `vim.Diagnostic`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":160,"character":73},"end":{"line":160,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":37},"end":{"line":224,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":310,"character":16},"end":{"line":310,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":326,"character":19},"end":{"line":326,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":367,"character":19},"end":{"line":367,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":384,"character":19},"end":{"line":384,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":438,"character":5},"end":{"line":438,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":448,"character":19},"end":{"line":448,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":16},"end":{"line":563,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":698,"character":16},"end":{"line":698,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":437,"character":14},"end":{"line":437,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":6},"end":{"line":21,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":64},"end":{"line":283,"character":67}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":475,"character":27},"end":{"line":475,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":484,"character":30},"end":{"line":484,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":497,"character":27},"end":{"line":497,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":26},"end":{"line":563,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":700,"character":12},"end":{"line":700,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":798,"character":64},"end":{"line":798,"character":67}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":300,"character":10},"end":{"line":300,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":571,"character":19},"end":{"line":571,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":304,"character":34},"end":{"line":304,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":413,"character":17},"end":{"line":413,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":655,"character":23},"end":{"line":655,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":781,"character":42},"end":{"line":781,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":68},"end":{"line":299,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":769,"character":38},"end":{"line":769,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":49,"character":71},"end":{"line":49,"character":77}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/highlight.lua":[{"severity":2,"range":{"start":{"line":3,"character":19},"end":{"line":3,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":4},"end":{"line":6,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":4},"end":{"line":7,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":4},"end":{"line":8,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":4},"end":{"line":9,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":4},"end":{"line":10,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":4},"end":{"line":15,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":4},"end":{"line":16,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":4},"end":{"line":17,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":4},"end":{"line":20,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":4},"end":{"line":21,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":4},"end":{"line":22,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":4},"end":{"line":23,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":4},"end":{"line":25,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":4},"end":{"line":26,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":4},"end":{"line":27,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":4},"end":{"line":29,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":4},"end":{"line":30,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":4},"end":{"line":32,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":4},"end":{"line":36,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":4},"end":{"line":37,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":4},"end":{"line":38,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":4},"end":{"line":43,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":4},"end":{"line":44,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":4},"end":{"line":46,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":4},"end":{"line":47,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":4},"end":{"line":48,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":4},"end":{"line":49,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":4},"end":{"line":50,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":4},"end":{"line":51,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":4},"end":{"line":53,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":4},"end":{"line":54,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":4},"end":{"line":55,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":4},"end":{"line":58,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":4},"end":{"line":59,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":4},"end":{"line":60,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":4},"end":{"line":61,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/icons.lua":[{"severity":2,"range":{"start":{"line":89,"character":6},"end":{"line":89,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":8},"end":{"line":90,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":10},"end":{"line":92,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/loading_animation.lua":[{"severity":2,"range":{"start":{"line":28,"character":11},"end":{"line":28,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":10},"end":{"line":13,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":4},"end":{"line":100,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":28},"end":{"line":45,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":9},"end":{"line":34,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":62},"end":{"line":34,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/mention.lua":[{"severity":2,"range":{"start":{"line":103,"character":2},"end":{"line":103,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":27},"end":{"line":4,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":2},"end":{"line":104,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":2},"end":{"line":105,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":8},"end":{"line":11,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":6},"end":{"line":33,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":23},"end":{"line":94,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":4},"end":{"line":112,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":32},"end":{"line":14,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":26},"end":{"line":56,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":7},"end":{"line":132,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":7},"end":{"line":137,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":116,"character":25},"end":{"line":116,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":27},"end":{"line":137,"character":31}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/navigation.lua":[{"severity":2,"range":{"start":{"line":53,"character":2},"end":{"line":53,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":2},"end":{"line":86,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":23},"end":{"line":32,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":34,"character":19},"end":{"line":34,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":21},"end":{"line":52,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":23},"end":{"line":66,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":19},"end":{"line":68,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":6},"end":{"line":47,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":6},"end":{"line":82,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output.lua":[{"severity":2,"range":{"start":{"line":94,"character":9},"end":{"line":94,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":9},"end":{"line":110,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":30},"end":{"line":141,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":30},"end":{"line":145,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output_window.lua":[{"severity":2,"range":{"start":{"line":4,"character":14},"end":{"line":4,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":2},"end":{"line":39,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":2},"end":{"line":40,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":2},"end":{"line":42,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":43,"character":2},"end":{"line":43,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":2},"end":{"line":44,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":2},"end":{"line":45,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":2},"end":{"line":47,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":2},"end":{"line":48,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":2},"end":{"line":49,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":2},"end":{"line":50,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":2},"end":{"line":51,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":2},"end":{"line":61,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":2},"end":{"line":89,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":2},"end":{"line":90,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":2},"end":{"line":91,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":2},"end":{"line":106,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":2},"end":{"line":141,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":158,"character":2},"end":{"line":158,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":2},"end":{"line":168,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":9},"end":{"line":69,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":4},"end":{"line":139,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":8},"end":{"line":148,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":8},"end":{"line":149,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":7,"character":21},"end":{"line":7,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":22},"end":{"line":58,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":12},"end":{"line":132,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":11},"end":{"line":30,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":6},"end":{"line":162,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":172,"character":6},"end":{"line":172,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua":[{"severity":2,"range":{"start":{"line":656,"character":2},"end":{"line":656,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":733,"character":2},"end":{"line":733,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":4},"end":{"line":507,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":685,"character":4},"end":{"line":685,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":699,"character":4},"end":{"line":699,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":194,"character":18},"end":{"line":194,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":17},"end":{"line":195,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":21},"end":{"line":197,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":4},"end":{"line":213,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":669,"character":9},"end":{"line":669,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":31},"end":{"line":189,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":453,"character":30},"end":{"line":453,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":54},"end":{"line":507,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":507,"character":73},"end":{"line":507,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":685,"character":36},"end":{"line":685,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":316,"character":23},"end":{"line":316,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":9},"end":{"line":30,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":9},"end":{"line":32,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/session_picker.lua":[{"severity":2,"range":{"start":{"line":423,"character":2},"end":{"line":423,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":16},"end":{"line":233,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":25},"end":{"line":53,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":365,"character":6},"end":{"line":365,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":396,"character":6},"end":{"line":396,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":401,"character":6},"end":{"line":401,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":8},"end":{"line":342,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":10},"end":{"line":324,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":8},"end":{"line":402,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":22},"end":{"line":249,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":51},"end":{"line":402,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":402,"character":69},"end":{"line":402,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":22},"end":{"line":54,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":253,"character":20},"end":{"line":253,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":20},"end":{"line":276,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":343,"character":22},"end":{"line":343,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":24},"end":{"line":107,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/timer.lua":[{"severity":2,"range":{"start":{"line":31,"character":18},"end":{"line":31,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":16},"end":{"line":25,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":53,"character":15},"end":{"line":53,"character":21}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":32,"character":45},"end":{"line":32,"character":51}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/helpers.lua":[{"severity":2,"range":{"start":{"line":112,"character":2},"end":{"line":112,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":2},"end":{"line":64,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":2},"end":{"line":78,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":2},"end":{"line":98,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":109,"character":26},"end":{"line":109,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":59,"character":2},"end":{"line":59,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":25},"end":{"line":96,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":9},"end":{"line":65,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":8},"end":{"line":91,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":4},"end":{"line":102,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":157,"character":9},"end":{"line":157,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":10},"end":{"line":237,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":21},"end":{"line":249,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":19},"end":{"line":45,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":6},"end":{"line":122,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":264,"character":14},"end":{"line":264,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":20},"end":{"line":39,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":15},"end":{"line":70,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":71,"character":10},"end":{"line":71,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":7},"end":{"line":86,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":25},"end":{"line":84,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":87,"character":12},"end":{"line":87,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":12},"end":{"line":262,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":263,"character":15},"end":{"line":263,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":177,"character":28},"end":{"line":177,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":17},"end":{"line":180,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":39},"end":{"line":197,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":36},"end":{"line":199,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/manual/init_replay.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":0},"end":{"line":21,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":0},"end":{"line":22,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":0},"end":{"line":23,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":0},"end":{"line":27,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":0},"end":{"line":19,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":0},"end":{"line":24,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":0,"character":20},"end":{"line":0,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":2},"end":{"line":16,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":3},"end":{"line":8,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":5},"end":{"line":10,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/manual/renderer_replay.lua":[{"severity":2,"range":{"start":{"line":35,"character":2},"end":{"line":35,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":2},"end":{"line":134,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":2},"end":{"line":200,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":2},"end":{"line":205,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":2},"end":{"line":265,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":266,"character":2},"end":{"line":266,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":284,"character":2},"end":{"line":284,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":289,"character":2},"end":{"line":289,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":293,"character":2},"end":{"line":293,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":2},"end":{"line":298,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":303,"character":2},"end":{"line":303,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":2},"end":{"line":307,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":311,"character":2},"end":{"line":311,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":315,"character":2},"end":{"line":315,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":2},"end":{"line":319,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":331,"character":2},"end":{"line":331,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":336,"character":2},"end":{"line":336,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":337,"character":2},"end":{"line":337,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":338,"character":2},"end":{"line":338,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":339,"character":2},"end":{"line":339,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":340,"character":2},"end":{"line":340,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":341,"character":2},"end":{"line":341,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":4},"end":{"line":112,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":152,"character":4},"end":{"line":152,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":4},"end":{"line":181,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":4},"end":{"line":189,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":4},"end":{"line":262,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":4},"end":{"line":356,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":20},"end":{"line":16,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":35,"character":69},"end":{"line":35,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":6},"end":{"line":70,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":21},"end":{"line":134,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":16},"end":{"line":162,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":163,"character":19},"end":{"line":163,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":14},"end":{"line":168,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":57},"end":{"line":200,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":207,"character":4},"end":{"line":207,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":246,"character":4},"end":{"line":246,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":14},"end":{"line":255,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":256,"character":15},"end":{"line":256,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":257,"character":21},"end":{"line":257,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":4},"end":{"line":333,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":27},"end":{"line":26,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":176,"character":6},"end":{"line":176,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":47},"end":{"line":19,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":61},"end":{"line":28,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":152,"character":45},"end":{"line":152,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":173,"character":17},"end":{"line":173,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":49},"end":{"line":181,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":59},"end":{"line":189,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":6},"end":{"line":213,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":54},"end":{"line":258,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":325,"character":6},"end":{"line":325,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":79},"end":{"line":356,"character":82}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":6},"end":{"line":48,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":45},"end":{"line":70,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":18},"end":{"line":222,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":223,"character":21},"end":{"line":223,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":81},"end":{"line":333,"character":84}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":12},"end":{"line":54,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":12},"end":{"line":55,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":176,"character":66},"end":{"line":176,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":208,"character":13},"end":{"line":208,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":8},"end":{"line":51,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":8},"end":{"line":52,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":325,"character":61},"end":{"line":325,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/init.lua":[{"severity":2,"range":{"start":{"line":32,"character":0},"end":{"line":32,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":0},"end":{"line":9,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":0},"end":{"line":26,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":27},"end":{"line":4,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":24},"end":{"line":5,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":20},"end":{"line":8,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":3},"end":{"line":15,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":5},"end":{"line":17,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":2},"end":{"line":62,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":2},"end":{"line":74,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":2},"end":{"line":80,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":4},"end":{"line":18,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":4},"end":{"line":63,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":4},"end":{"line":64,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":24},"end":{"line":13,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":22},"end":{"line":17,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":4},"end":{"line":28,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":4},"end":{"line":65,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":26},"end":{"line":27,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua":[{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":4,"character":2},"end":{"line":4,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":2},"end":{"line":15,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":2},"end":{"line":60,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua":[{"severity":2,"range":{"start":{"line":7,"character":0},"end":{"line":7,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":2},"end":{"line":26,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":2},"end":{"line":114,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":2},"end":{"line":144,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":4},"end":{"line":31,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":4},"end":{"line":66,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":4},"end":{"line":86,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":115,"character":4},"end":{"line":115,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":4},"end":{"line":155,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":165,"character":4},"end":{"line":165,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":4},"end":{"line":175,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":4},"end":{"line":187,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":198,"character":4},"end":{"line":198,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":4},"end":{"line":205,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":9},"end":{"line":12,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":6},"end":{"line":90,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":22},"end":{"line":94,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":31},"end":{"line":199,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":66},"end":{"line":201,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":2},"end":{"line":18,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":51,"character":2},"end":{"line":51,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":2},"end":{"line":66,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":2},"end":{"line":81,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":4},"end":{"line":10,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":4},"end":{"line":19,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":24},"end":{"line":9,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":16},"end":{"line":61,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":16},"end":{"line":62,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":16},"end":{"line":63,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":16},"end":{"line":76,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":17},"end":{"line":77,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":16},"end":{"line":78,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":23},"end":{"line":35,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":52,"character":23},"end":{"line":59,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":67,"character":23},"end":{"line":74,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":83,"character":23},"end":{"line":90,"character":5}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":2},"end":{"line":26,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":22},"end":{"line":11,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":20},"end":{"line":13,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua":[{"severity":2,"range":{"start":{"line":6,"character":0},"end":{"line":6,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":2},"end":{"line":32,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":109,"character":2},"end":{"line":109,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":2},"end":{"line":232,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":4},"end":{"line":21,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":4},"end":{"line":65,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":4},"end":{"line":81,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":4},"end":{"line":90,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":4},"end":{"line":106,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":4},"end":{"line":110,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":4},"end":{"line":121,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":137,"character":4},"end":{"line":137,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":157,"character":4},"end":{"line":157,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":177,"character":4},"end":{"line":177,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":198,"character":4},"end":{"line":198,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":211,"character":4},"end":{"line":211,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":223,"character":4},"end":{"line":223,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":4},"end":{"line":233,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":24},"end":{"line":37,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":32},"end":{"line":41,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":18},"end":{"line":42,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":4},"end":{"line":69,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":4},"end":{"line":73,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":4},"end":{"line":77,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":4},"end":{"line":102,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":4},"end":{"line":103,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":4},"end":{"line":104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":32},"end":{"line":38,"character":35}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":36},"end":{"line":39,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":36},"end":{"line":40,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":6},"end":{"line":212,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/util.lua":[{"severity":2,"range":{"start":{"line":61,"character":9},"end":{"line":61,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":18},"end":{"line":15,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":16},"end":{"line":27,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":16},"end":{"line":281,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":414,"character":9},"end":{"line":414,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":419,"character":9},"end":{"line":419,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":450,"character":24},"end":{"line":450,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":19},"end":{"line":14,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":19},"end":{"line":16,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":253,"character":25},"end":{"line":253,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":404,"character":20},"end":{"line":404,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":237,"character":12},"end":{"line":237,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":28},"end":{"line":322,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":16},"end":{"line":247,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":21},"end":{"line":319,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":343,"character":20},"end":{"line":343,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":203,"character":27},"end":{"line":203,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":37},"end":{"line":255,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":31},"end":{"line":258,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":41},"end":{"line":319,"character":49}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 1.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":282,"character":7},"end":{"line":282,"character":13}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":238,"character":11},"end":{"line":238,"character":17}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/state.lua":[{"severity":2,"range":{"start":{"line":133,"character":2},"end":{"line":133,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":161,"character":14},"end":{"line":161,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":174,"character":14},"end":{"line":174,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":11},"end":{"line":195,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":10},"end":{"line":138,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":10},"end":{"line":187,"character":12}},"message":"Missing required fields in type `OpencodeState`: `input_content`, `is_opencode_focused`, `current_mode`, `last_output`, `restore_points`, `cost`, `tokens_count`, `job_count`, `api_client`, `required_version`, `append`, `remove`, `subscribe`, `subscribe`, `unsubscribe`, `is_running`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua":[{"severity":2,"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":8,"character":2},"end":{"line":8,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":2},"end":{"line":13,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":2},"end":{"line":46,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":2},"end":{"line":114,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":4},"end":{"line":9,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":4},"end":{"line":68,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":117,"character":4},"end":{"line":117,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":22},"end":{"line":6,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":4},"end":{"line":108,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":4},"end":{"line":156,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":6},"end":{"line":26,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/session.lua":[{"severity":2,"range":{"start":{"line":54,"character":9},"end":{"line":54,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":4},"end":{"line":11,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":4},"end":{"line":50,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":24},"end":{"line":114,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":15},"end":{"line":20,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":15},"end":{"line":28,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":15},"end":{"line":36,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":15},"end":{"line":94,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":135,"character":24},"end":{"line":135,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":33},"end":{"line":145,"character":36}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":69},"end":{"line":11,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":51},"end":{"line":50,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":72},"end":{"line":50,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":72,"character":17},"end":{"line":72,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":31},"end":{"line":144,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":178,"character":50},"end":{"line":178,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":31},"end":{"line":95,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":46},"end":{"line":95,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/context.lua":[{"severity":2,"range":{"start":{"line":206,"character":2},"end":{"line":206,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":2},"end":{"line":224,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":246,"character":4},"end":{"line":246,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":4},"end":{"line":247,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":4},"end":{"line":248,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":288,"character":4},"end":{"line":288,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":24},"end":{"line":74,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":18},"end":{"line":206,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":209,"character":15},"end":{"line":209,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":4},"end":{"line":218,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":18},"end":{"line":224,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":12},"end":{"line":50,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":12},"end":{"line":53,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":12},"end":{"line":56,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":24},"end":{"line":265,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":270,"character":7},"end":{"line":270,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":208,"character":24},"end":{"line":208,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":23},"end":{"line":106,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":251,"character":14},"end":{"line":251,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":26},"end":{"line":267,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":59},"end":{"line":96,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":37},"end":{"line":218,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":18},"end":{"line":206,"character":76}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":78},"end":{"line":206,"character":81}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":18},"end":{"line":224,"character":72}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":74},"end":{"line":224,"character":77}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":58},"end":{"line":206,"character":62}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":64},"end":{"line":206,"character":69}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":206,"character":71},"end":{"line":206,"character":75}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":54},"end":{"line":224,"character":58}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":60},"end":{"line":224,"character":65}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":224,"character":67},"end":{"line":224,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"data":{"versions":["Lua 5.1","LuaJIT"]},"tags":[2],"range":{"start":{"line":208,"character":17},"end":{"line":208,"character":23}},"message":"Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)","source":"Lua Diagnostics.","code":"deprecated"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/input_window.lua":[{"severity":2,"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":2},"end":{"line":54,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":55,"character":2},"end":{"line":55,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":2},"end":{"line":95,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":97,"character":2},"end":{"line":97,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":2},"end":{"line":98,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":2},"end":{"line":100,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":2},"end":{"line":101,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":2},"end":{"line":104,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":2},"end":{"line":105,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":2},"end":{"line":106,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":2},"end":{"line":126,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":2},"end":{"line":170,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":2},"end":{"line":179,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":197,"character":2},"end":{"line":197,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":206,"character":2},"end":{"line":206,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":225,"character":2},"end":{"line":225,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":2},"end":{"line":245,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":2},"end":{"line":255,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":8},"end":{"line":44,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":8},"end":{"line":45,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":16},"end":{"line":81,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":22},"end":{"line":83,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":4},"end":{"line":90,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":195,"character":50},"end":{"line":195,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":20},"end":{"line":5,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":22},"end":{"line":22,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":22},"end":{"line":121,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":23},"end":{"line":122,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":146,"character":4},"end":{"line":146,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":4},"end":{"line":162,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":54},"end":{"line":170,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":16},"end":{"line":181,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":215,"character":16},"end":{"line":215,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":20},"end":{"line":218,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":235,"character":16},"end":{"line":235,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":19},"end":{"line":88,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":31,"character":11},"end":{"line":31,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":37},"end":{"line":53,"character":40}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":90,"character":43},"end":{"line":90,"character":46}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":135,"character":18},"end":{"line":135,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":18},"end":{"line":138,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":22},"end":{"line":139,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":56},"end":{"line":162,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":26},"end":{"line":258,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/ui.lua":[{"severity":2,"range":{"start":{"line":234,"character":2},"end":{"line":234,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":2},"end":{"line":68,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":73,"character":2},"end":{"line":73,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":2},"end":{"line":74,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":117,"character":2},"end":{"line":117,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":2},"end":{"line":136,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":8},"end":{"line":22,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":8},"end":{"line":23,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":8},"end":{"line":25,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":26,"character":8},"end":{"line":26,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":8},"end":{"line":27,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":8},"end":{"line":28,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":54,"character":4},"end":{"line":54,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":58,"character":9},"end":{"line":58,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":2},"end":{"line":80,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":4},"end":{"line":39,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":22},"end":{"line":147,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":16},"end":{"line":164,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":4},"end":{"line":191,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":218,"character":22},"end":{"line":218,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":18},"end":{"line":38,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":120,"character":10},"end":{"line":120,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":10},"end":{"line":139,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":42},"end":{"line":85,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":29},"end":{"line":86,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":5},"end":{"line":122,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":124,"character":6},"end":{"line":124,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":123,"character":7},"end":{"line":123,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/git_review.lua":[{"severity":2,"range":{"start":{"line":10,"character":16},"end":{"line":10,"character":30}},"message":"Undefined type or alias `vim.SystemOpts`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":19,"character":2},"end":{"line":19,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":2},"end":{"line":107,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":356,"character":2},"end":{"line":356,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":4},"end":{"line":15,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":22,"character":11},"end":{"line":22,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":106,"character":20},"end":{"line":106,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":2},"end":{"line":292,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":17},"end":{"line":20,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":2},"end":{"line":260,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":4},"end":{"line":128,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":4},"end":{"line":141,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":6},"end":{"line":224,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":233,"character":6},"end":{"line":233,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":234,"character":6},"end":{"line":234,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":4},"end":{"line":249,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":261,"character":4},"end":{"line":261,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":283,"character":4},"end":{"line":283,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":4},"end":{"line":299,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":318,"character":4},"end":{"line":318,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":373,"character":6},"end":{"line":373,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":375,"character":6},"end":{"line":375,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":8},"end":{"line":68,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":8},"end":{"line":74,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":80,"character":8},"end":{"line":80,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":4},"end":{"line":149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":25},"end":{"line":212,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":213,"character":21},"end":{"line":213,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":310,"character":4},"end":{"line":310,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":49},"end":{"line":78,"character":52}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":6},"end":{"line":150,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":172,"character":6},"end":{"line":172,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":190,"character":6},"end":{"line":190,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":6},"end":{"line":255,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":329,"character":4},"end":{"line":329,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":271,"character":8},"end":{"line":271,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":305,"character":6},"end":{"line":305,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":6},"end":{"line":324,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":330,"character":6},"end":{"line":330,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":373,"character":72},"end":{"line":373,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":375,"character":76},"end":{"line":375,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":29},"end":{"line":40,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":228,"character":7},"end":{"line":228,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":5},"end":{"line":287,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":13},"end":{"line":262,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":340,"character":10},"end":{"line":340,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":15},"end":{"line":151,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":331,"character":15},"end":{"line":331,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/server_job.lua":[{"severity":2,"range":{"start":{"line":100,"character":12},"end":{"line":100,"character":15}},"message":"Undefined type or alias `Job`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":11,"character":35},"end":{"line":11,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":6},"end":{"line":114,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":8},"end":{"line":56,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":25},"end":{"line":64,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":8},"end":{"line":118,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":124,"character":25},"end":{"line":124,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":51},"end":{"line":114,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":69},"end":{"line":114,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":10},"end":{"line":57,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":76},"end":{"line":118,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":12},"end":{"line":39,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":12},"end":{"line":46,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":62},"end":{"line":57,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":14},"end":{"line":40,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":14},"end":{"line":47,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":72},"end":{"line":40,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":47,"character":66},"end":{"line":47,"character":69}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua":[{"severity":2,"range":{"start":{"line":21,"character":0},"end":{"line":21,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":2},"end":{"line":24,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":92,"character":2},"end":{"line":92,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":100,"character":2},"end":{"line":100,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":111,"character":2},"end":{"line":111,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":2},"end":{"line":242,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":421,"character":2},"end":{"line":421,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":486,"character":2},"end":{"line":486,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":4},"end":{"line":114,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":4},"end":{"line":119,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":4},"end":{"line":129,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":4},"end":{"line":162,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":4},"end":{"line":180,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":4},"end":{"line":199,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":4},"end":{"line":212,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":4},"end":{"line":245,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":274,"character":4},"end":{"line":274,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":280,"character":4},"end":{"line":280,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":287,"character":4},"end":{"line":287,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":306,"character":4},"end":{"line":306,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":338,"character":4},"end":{"line":338,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":370,"character":4},"end":{"line":370,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":396,"character":4},"end":{"line":396,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":422,"character":4},"end":{"line":422,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":446,"character":4},"end":{"line":446,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":470,"character":4},"end":{"line":470,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":487,"character":4},"end":{"line":487,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":509,"character":4},"end":{"line":509,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":6},"end":{"line":249,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":6},"end":{"line":258,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":269,"character":6},"end":{"line":269,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":6},"end":{"line":250,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":251,"character":6},"end":{"line":251,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":259,"character":6},"end":{"line":259,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":262,"character":6},"end":{"line":262,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":265,"character":6},"end":{"line":265,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":15},"end":{"line":249,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":16},"end":{"line":258,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":173,"character":30},"end":{"line":173,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":188,"character":29},"end":{"line":188,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":526,"character":11},"end":{"line":526,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":0},"end":{"line":40,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":0},"end":{"line":50,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":0},"end":{"line":94,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":111,"character":0},"end":{"line":111,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":23,"character":2},"end":{"line":23,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":2},"end":{"line":45,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":2},"end":{"line":52,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":2},"end":{"line":65,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":69,"character":2},"end":{"line":69,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":2},"end":{"line":75,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":95,"character":2},"end":{"line":95,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":112,"character":2},"end":{"line":112,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":118,"character":2},"end":{"line":118,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":125,"character":2},"end":{"line":125,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":132,"character":2},"end":{"line":132,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":2},"end":{"line":136,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":4},"end":{"line":119,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":4},"end":{"line":126,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":97,"character":35},"end":{"line":97,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":42},"end":{"line":104,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":54},"end":{"line":105,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":17},"end":{"line":12,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua":[{"severity":3,"range":{"start":{"line":246,"character":8},"end":{"line":246,"character":21}},"message":"Global variable in lowercase initial, Did you miss `local` or misspell it?","source":"Lua Diagnostics.","code":"lowercase-global"},{"severity":2,"range":{"start":{"line":30,"character":0},"end":{"line":30,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":2},"end":{"line":36,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":2},"end":{"line":99,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":2},"end":{"line":130,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":174,"character":2},"end":{"line":174,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":199,"character":2},"end":{"line":199,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":263,"character":2},"end":{"line":263,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":4},"end":{"line":103,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":131,"character":4},"end":{"line":131,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":144,"character":4},"end":{"line":144,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":151,"character":4},"end":{"line":151,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":4},"end":{"line":175,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":4},"end":{"line":200,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":221,"character":4},"end":{"line":221,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":240,"character":4},"end":{"line":240,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":278,"character":4},"end":{"line":278,"character":15}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":284,"character":4},"end":{"line":284,"character":14}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":290,"character":4},"end":{"line":290,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":297,"character":4},"end":{"line":297,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":4},"end":{"line":307,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":317,"character":4},"end":{"line":317,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":22},"end":{"line":38,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":24},"end":{"line":40,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":42,"character":4},"end":{"line":42,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":104,"character":4},"end":{"line":104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":39,"character":26},"end":{"line":39,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":37,"character":21},"end":{"line":37,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":285,"character":6},"end":{"line":285,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":301,"character":6},"end":{"line":301,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":311,"character":6},"end":{"line":311,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":321,"character":6},"end":{"line":321,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":214,"character":6},"end":{"line":214,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":279,"character":24},"end":{"line":279,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":286,"character":6},"end":{"line":286,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":6},"end":{"line":291,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":6},"end":{"line":298,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":308,"character":6},"end":{"line":308,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":318,"character":6},"end":{"line":318,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":280,"character":28},"end":{"line":280,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":11,"character":21},"end":{"line":27,"character":3}},"message":"Missing required fields in type `OpencodeApiClient`: `base_url`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":88,"character":28},"end":{"line":94,"character":5}},"message":"Missing required fields in type `OpencodeServer`: `spawn_promise`, `shutdown_promise`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":146,"character":29},"end":{"line":146,"character":51}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":202,"character":29},"end":{"line":202,"character":45}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":243,"character":29},"end":{"line":243,"character":45}},"message":"Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":254,"character":20},"end":{"line":254,"character":58}},"message":"Missing required fields in type `OpencodeContextConfig`: `enabled`, `cursor_data`, `diagnostics`, `selection`, `agents`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/curl.lua":[{"severity":2,"range":{"start":{"line":39,"character":16},"end":{"line":39,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":134,"character":4},"end":{"line":134,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":81,"character":16},"end":{"line":81,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":126,"character":15},"end":{"line":126,"character":16}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":126,"character":21},"end":{"line":126,"character":30}},"message":"This function expects a maximum of 0 argument(s) but instead it is receiving 2.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api_client.lua":[{"severity":2,"range":{"start":{"line":382,"character":12},"end":{"line":382,"character":15}},"message":"Undefined type or alias `Job`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":223,"character":164},"end":{"line":223,"character":168}},"message":"Undefined type or alias `Part`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":393,"character":28},"end":{"line":393,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":393,"character":45},"end":{"line":393,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua":[{"severity":2,"data":"syntax","range":{"start":{"line":148,"character":118},"end":{"line":148,"character":118}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":149,"character":121},"end":{"line":149,"character":121}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":150,"character":108},"end":{"line":150,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":151,"character":108},"end":{"line":151,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":152,"character":117},"end":{"line":152,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":153,"character":117},"end":{"line":153,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":154,"character":112},"end":{"line":154,"character":112}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":155,"character":102},"end":{"line":155,"character":102}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":156,"character":108},"end":{"line":156,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":157,"character":108},"end":{"line":157,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":158,"character":104},"end":{"line":158,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":159,"character":114},"end":{"line":159,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":160,"character":114},"end":{"line":160,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":161,"character":100},"end":{"line":161,"character":100}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":162,"character":117},"end":{"line":162,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":163,"character":110},"end":{"line":163,"character":110}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":164,"character":104},"end":{"line":164,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":165,"character":115},"end":{"line":165,"character":115}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":166,"character":109},"end":{"line":166,"character":109}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":167,"character":113},"end":{"line":167,"character":113}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":168,"character":126},"end":{"line":168,"character":126}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":181,"character":118},"end":{"line":181,"character":118}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":182,"character":121},"end":{"line":182,"character":121}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":183,"character":108},"end":{"line":183,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":184,"character":108},"end":{"line":184,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":185,"character":117},"end":{"line":185,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":186,"character":117},"end":{"line":186,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":187,"character":112},"end":{"line":187,"character":112}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":188,"character":102},"end":{"line":188,"character":102}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":189,"character":108},"end":{"line":189,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":190,"character":108},"end":{"line":190,"character":108}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":191,"character":104},"end":{"line":191,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":192,"character":114},"end":{"line":192,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":193,"character":114},"end":{"line":193,"character":114}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":194,"character":100},"end":{"line":194,"character":100}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":195,"character":117},"end":{"line":195,"character":117}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":196,"character":110},"end":{"line":196,"character":110}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":197,"character":104},"end":{"line":197,"character":104}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":198,"character":115},"end":{"line":198,"character":115}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":199,"character":109},"end":{"line":199,"character":109}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":200,"character":113},"end":{"line":200,"character":113}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"data":"syntax","range":{"start":{"line":201,"character":126},"end":{"line":201,"character":126}},"message":"`)` expected.","source":"Lua Syntax Check.","code":"luadoc-miss-symbol"},{"severity":2,"range":{"start":{"line":281,"character":39},"end":{"line":281,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":308,"character":10},"end":{"line":308,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":361,"character":8},"end":{"line":361,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":2},"end":{"line":15,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":2},"end":{"line":21,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":2},"end":{"line":41,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":2},"end":{"line":64,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":2},"end":{"line":77,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":2},"end":{"line":93,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":102,"character":2},"end":{"line":102,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":113,"character":2},"end":{"line":113,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":56,"character":4},"end":{"line":56,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua":[{"severity":2,"range":{"start":{"line":2,"character":0},"end":{"line":2,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":2},"end":{"line":14,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":25,"character":2},"end":{"line":25,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":2},"end":{"line":30,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":2},"end":{"line":36,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":2},"end":{"line":49,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":60,"character":2},"end":{"line":60,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":9,"character":19},"end":{"line":9,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":10,"character":19},"end":{"line":10,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":19},"end":{"line":20,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":19},"end":{"line":21,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua":[{"severity":2,"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":4}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua":[{"severity":2,"range":{"start":{"line":339,"character":2},"end":{"line":339,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":16,"character":28},"end":{"line":16,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":4},"end":{"line":101,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":17},"end":{"line":107,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":4},"end":{"line":130,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":28},"end":{"line":169,"character":31}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":250,"character":4},"end":{"line":250,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":4},"end":{"line":267,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":272,"character":4},"end":{"line":272,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":4},"end":{"line":291,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":299,"character":4},"end":{"line":299,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":6},"end":{"line":145,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":298,"character":9},"end":{"line":298,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":6},"end":{"line":61,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":249,"character":5},"end":{"line":249,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":6},"end":{"line":319,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":59},"end":{"line":101,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":130,"character":55},"end":{"line":130,"character":58}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":6},"end":{"line":202,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":6},"end":{"line":252,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":267,"character":97},"end":{"line":267,"character":100}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":274,"character":6},"end":{"line":274,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":291,"character":39},"end":{"line":291,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":301,"character":6},"end":{"line":301,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":326,"character":24},"end":{"line":326,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":57},"end":{"line":145,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":75},"end":{"line":145,"character":78}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":8},"end":{"line":222,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":8},"end":{"line":226,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":258,"character":19},"end":{"line":258,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":70},"end":{"line":61,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":6},"end":{"line":156,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":319,"character":46},"end":{"line":319,"character":49}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":62},"end":{"line":202,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":222,"character":38},"end":{"line":222,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":51},"end":{"line":156,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":69},"end":{"line":156,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":59},"end":{"line":136,"character":64}},"message":"Undefined field `parts`.","source":"Lua Diagnostics.","code":"undefined-field"},{"severity":2,"range":{"start":{"line":136,"character":38},"end":{"line":136,"character":42}},"message":"Undefined field `info`.","source":"Lua Diagnostics.","code":"undefined-field"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/health.lua":[{"severity":2,"range":{"start":{"line":2,"character":15},"end":{"line":2,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":6,"character":9},"end":{"line":6,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":9},"end":{"line":114,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":14},"end":{"line":154,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":14,"character":17},"end":{"line":14,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":148,"character":5},"end":{"line":148,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":18},"end":{"line":147,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/history.lua":[{"severity":2,"range":{"start":{"line":9,"character":29},"end":{"line":9,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/id.lua":[{"severity":2,"range":{"start":{"line":139,"character":9},"end":{"line":139,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":9},"end":{"line":101,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":119,"character":11},"end":{"line":119,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":39},"end":{"line":66,"character":42}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/keymap.lua":[{"severity":2,"range":{"start":{"line":38,"character":9},"end":{"line":38,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":9},"end":{"line":49,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":15,"character":19},"end":{"line":15,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":8},"end":{"line":21,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":74,"character":12},"end":{"line":74,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":12},"end":{"line":75,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":8},"end":{"line":19,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":8},"end":{"line":65,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":98},"end":{"line":21,"character":101}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/opencode_server.lua":[{"severity":2,"range":{"start":{"line":58,"character":34},"end":{"line":58,"character":53}},"message":"Undefined type or alias `vim.SystemCompleted`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":13},"end":{"line":66,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":12},"end":{"line":18,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/provider.lua":[{"severity":2,"range":{"start":{"line":22,"character":2},"end":{"line":22,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/promise.lua":[{"severity":2,"range":{"start":{"line":53,"character":10},"end":{"line":53,"character":15}},"message":"Undefined param `error`.","source":"Lua Diagnostics.","code":"undefined-doc-param"},{"severity":2,"range":{"start":{"line":5,"character":77},"end":{"line":5,"character":78}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":2,"character":37},"end":{"line":2,"character":38}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":5,"character":34},"end":{"line":5,"character":35}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":2,"character":62},"end":{"line":2,"character":63}},"message":"Undefined type or alias `T`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":42,"character":24},"end":{"line":42,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":25},"end":{"line":62,"character":28}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":178,"character":18},"end":{"line":178,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":103,"character":26},"end":{"line":103,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":27},"end":{"line":150,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua":[{"severity":2,"range":{"start":{"line":6,"character":0},"end":{"line":6,"character":8}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":13}},"message":"Undefined global `before_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":2},"end":{"line":38,"character":12}},"message":"Undefined global `after_each`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":44,"character":2},"end":{"line":44,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":2},"end":{"line":242,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":359,"character":2},"end":{"line":359,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":425,"character":2},"end":{"line":425,"character":10}},"message":"Undefined global `describe`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":4},"end":{"line":33,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":41,"character":4},"end":{"line":41,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":45,"character":4},"end":{"line":45,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":4},"end":{"line":79,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":121,"character":4},"end":{"line":121,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":243,"character":4},"end":{"line":243,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":276,"character":4},"end":{"line":276,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":309,"character":4},"end":{"line":309,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":4},"end":{"line":342,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":360,"character":4},"end":{"line":360,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":393,"character":4},"end":{"line":393,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":426,"character":4},"end":{"line":426,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":465,"character":4},"end":{"line":465,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":496,"character":4},"end":{"line":496,"character":6}},"message":"Undefined global `it`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":21,"character":23},"end":{"line":21,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":24,"character":4},"end":{"line":24,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":26},"end":{"line":20,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":281,"character":6},"end":{"line":281,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":306,"character":6},"end":{"line":306,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":365,"character":6},"end":{"line":365,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":389,"character":6},"end":{"line":389,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":278,"character":30},"end":{"line":278,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":362,"character":30},"end":{"line":362,"character":33}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":390,"character":6},"end":{"line":390,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":421,"character":6},"end":{"line":421,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":461,"character":6},"end":{"line":461,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":493,"character":6},"end":{"line":493,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":521,"character":6},"end":{"line":521,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":456,"character":21},"end":{"line":456,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":457,"character":21},"end":{"line":457,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":458,"character":21},"end":{"line":458,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":295,"character":19},"end":{"line":295,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":369,"character":20},"end":{"line":369,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":378,"character":19},"end":{"line":378,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":394,"character":20},"end":{"line":394,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":430,"character":20},"end":{"line":430,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":469,"character":20},"end":{"line":469,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":500,"character":20},"end":{"line":500,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":428,"character":33},"end":{"line":428,"character":48}},"message":"Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`","source":"Lua Diagnostics.","code":"missing-fields"},{"severity":2,"range":{"start":{"line":467,"character":33},"end":{"line":467,"character":48}},"message":"Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`","source":"Lua Diagnostics.","code":"missing-fields"}],"/home/francis/Projects/_nvim/opencode.nvim/plugin/opencode.lua":[{"severity":2,"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":3}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1,"character":3},"end":{"line":1,"character":6}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/topbar.lua":[{"severity":2,"range":{"start":{"line":96,"character":2},"end":{"line":96,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":2},"end":{"line":79,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":65,"character":16},"end":{"line":65,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":68,"character":10},"end":{"line":68,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":18},"end":{"line":64,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":72,"character":9},"end":{"line":72,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":105,"character":4},"end":{"line":105,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":107,"character":4},"end":{"line":107,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":108,"character":86},"end":{"line":108,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/review.lua":[{"severity":2,"range":{"start":{"line":71,"character":2},"end":{"line":71,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":2},"end":{"line":140,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":192,"character":2},"end":{"line":192,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":182,"character":2},"end":{"line":182,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":145,"character":4},"end":{"line":145,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":146,"character":4},"end":{"line":146,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":147,"character":4},"end":{"line":147,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":149,"character":4},"end":{"line":149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":150,"character":4},"end":{"line":150,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":48,"character":14},"end":{"line":48,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":19},"end":{"line":49,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":87,"character":4},"end":{"line":87,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":88,"character":10},"end":{"line":88,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":110,"character":17},"end":{"line":110,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":141,"character":17},"end":{"line":141,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":180,"character":18},"end":{"line":180,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":181,"character":22},"end":{"line":181,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":20},"end":{"line":191,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":307,"character":2},"end":{"line":307,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":76,"character":22},"end":{"line":76,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":77,"character":10},"end":{"line":77,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":175,"character":6},"end":{"line":175,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":19},"end":{"line":19,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":63,"character":26},"end":{"line":63,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":18},"end":{"line":64,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":66,"character":6},"end":{"line":66,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":79,"character":20},"end":{"line":79,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":202,"character":4},"end":{"line":202,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":292,"character":4},"end":{"line":292,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":324,"character":4},"end":{"line":324,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":333,"character":4},"end":{"line":333,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":38,"character":8},"end":{"line":38,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":50,"character":18},"end":{"line":50,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":18},"end":{"line":83,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":9},"end":{"line":91,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":158,"character":8},"end":{"line":158,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":162,"character":8},"end":{"line":162,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":163,"character":8},"end":{"line":163,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":8},"end":{"line":164,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":167,"character":8},"end":{"line":167,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":8},"end":{"line":168,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":171,"character":8},"end":{"line":171,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":4},"end":{"line":210,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":312,"character":23},"end":{"line":312,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":313,"character":19},"end":{"line":313,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":31},"end":{"line":57,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":24},"end":{"line":155,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":159,"character":26},"end":{"line":159,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":18},"end":{"line":210,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":224,"character":6},"end":{"line":224,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":239,"character":6},"end":{"line":239,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":29},"end":{"line":13,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":101,"character":16},"end":{"line":101,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":296,"character":5},"end":{"line":296,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":328,"character":5},"end":{"line":328,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":93,"character":29},"end":{"line":93,"character":32}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":13},"end":{"line":94,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/snapshot.lua":[{"severity":2,"range":{"start":{"line":8,"character":16},"end":{"line":8,"character":30}},"message":"Undefined type or alias `vim.SystemOpts`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":17,"character":2},"end":{"line":17,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":217,"character":2},"end":{"line":217,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":236,"character":2},"end":{"line":236,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":2},"end":{"line":252,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":61,"character":9},"end":{"line":61,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":164,"character":9},"end":{"line":164,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":194,"character":4},"end":{"line":194,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":13,"character":4},"end":{"line":13,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":30,"character":4},"end":{"line":30,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":4},"end":{"line":40,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":46,"character":4},"end":{"line":46,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":4},"end":{"line":52,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":4},"end":{"line":57,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":4},"end":{"line":70,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":4},"end":{"line":91,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":4},"end":{"line":122,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":4},"end":{"line":128,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":133,"character":4},"end":{"line":133,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":4},"end":{"line":154,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":160,"character":4},"end":{"line":160,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":210,"character":4},"end":{"line":210,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":4},"end":{"line":226,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":4},"end":{"line":232,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":4},"end":{"line":242,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":4},"end":{"line":248,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":259,"character":19},"end":{"line":259,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":20,"character":11},"end":{"line":20,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":27,"character":20},"end":{"line":27,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":85,"character":4},"end":{"line":85,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":138,"character":14},"end":{"line":138,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":20},"end":{"line":140,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":24},"end":{"line":168,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":171,"character":20},"end":{"line":171,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":187,"character":6},"end":{"line":187,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":205,"character":24},"end":{"line":205,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":214,"character":4},"end":{"line":214,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":236,"character":51},"end":{"line":236,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":252,"character":82},"end":{"line":252,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":17},"end":{"line":18,"character":20}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":5},"end":{"line":84,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":24},"end":{"line":89,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":191,"character":6},"end":{"line":191,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":40,"character":36},"end":{"line":40,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":52,"character":51},"end":{"line":52,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":57,"character":80},"end":{"line":57,"character":83}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":70,"character":36},"end":{"line":70,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":91,"character":57},"end":{"line":91,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":36},"end":{"line":122,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":56},"end":{"line":128,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":133,"character":72},"end":{"line":133,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":36},"end":{"line":154,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":160,"character":67},"end":{"line":160,"character":70}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":61},"end":{"line":179,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":212,"character":6},"end":{"line":212,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":78},"end":{"line":226,"character":81}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":232,"character":82},"end":{"line":232,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":242,"character":78},"end":{"line":242,"character":81}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":248,"character":82},"end":{"line":248,"character":85}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":189,"character":8},"end":{"line":189,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":89,"character":44},"end":{"line":89,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":184,"character":44},"end":{"line":184,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/files.lua":[{"severity":2,"range":{"start":{"line":75,"character":2},"end":{"line":75,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":32,"character":5},"end":{"line":32,"character":8}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":75,"character":83},"end":{"line":75,"character":86}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":82,"character":19},"end":{"line":82,"character":22}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":83,"character":14},"end":{"line":83,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":86,"character":20},"end":{"line":86,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":94,"character":15},"end":{"line":94,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":136,"character":11},"end":{"line":136,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":7},"end":{"line":169,"character":10}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":18,"character":27},"end":{"line":18,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":18},"end":{"line":62,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":18},"end":{"line":129,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":168,"character":24},"end":{"line":168,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":19,"character":16},"end":{"line":19,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":28,"character":13},"end":{"line":28,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":33,"character":14},"end":{"line":33,"character":17}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":36,"character":13},"end":{"line":36,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":184,"character":31},"end":{"line":184,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":22},"end":{"line":169,"character":25}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":169,"character":54},"end":{"line":169,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":170,"character":27},"end":{"line":170,"character":30}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":131,"character":13},"end":{"line":131,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api.lua":[{"severity":3,"range":{"start":{"line":650,"character":9},"end":{"line":650,"character":35}},"message":"Global variable in lowercase initial, Did you miss `local` or misspell it?","source":"Lua Diagnostics.","code":"lowercase-global"},{"severity":2,"range":{"start":{"line":207,"character":2},"end":{"line":207,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":2},"end":{"line":245,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":2},"end":{"line":260,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":385,"character":2},"end":{"line":385,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1175,"character":2},"end":{"line":1175,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":71,"character":9},"end":{"line":71,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":78,"character":9},"end":{"line":78,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":4},"end":{"line":179,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":4},"end":{"line":201,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":4},"end":{"line":322,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":332,"character":4},"end":{"line":332,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":4},"end":{"line":342,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":4},"end":{"line":354,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":358,"character":4},"end":{"line":358,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":363,"character":4},"end":{"line":363,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":461,"character":26},"end":{"line":461,"character":29}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":522,"character":4},"end":{"line":522,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":542,"character":4},"end":{"line":542,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":548,"character":4},"end":{"line":548,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":553,"character":4},"end":{"line":553,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":575,"character":4},"end":{"line":575,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":600,"character":4},"end":{"line":600,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":621,"character":4},"end":{"line":621,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":627,"character":4},"end":{"line":627,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":690,"character":4},"end":{"line":690,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":695,"character":4},"end":{"line":695,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":740,"character":4},"end":{"line":740,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1091,"character":15},"end":{"line":1091,"character":18}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1104,"character":4},"end":{"line":1104,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1109,"character":16},"end":{"line":1109,"character":19}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1149,"character":4},"end":{"line":1149,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":207,"character":65},"end":{"line":207,"character":68}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":239,"character":23},"end":{"line":239,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":24},"end":{"line":245,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":254,"character":23},"end":{"line":254,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":255,"character":38},"end":{"line":255,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":260,"character":24},"end":{"line":260,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":531,"character":6},"end":{"line":531,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":567,"character":6},"end":{"line":567,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":592,"character":6},"end":{"line":592,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":612,"character":6},"end":{"line":612,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":641,"character":6},"end":{"line":641,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":747,"character":6},"end":{"line":747,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1115,"character":11},"end":{"line":1115,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1128,"character":11},"end":{"line":1128,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1134,"character":11},"end":{"line":1134,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1102,"character":18},"end":{"line":1102,"character":21}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1113,"character":24},"end":{"line":1113,"character":27}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1150,"character":6},"end":{"line":1150,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1154,"character":6},"end":{"line":1154,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":179,"character":61},"end":{"line":179,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":201,"character":61},"end":{"line":201,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":322,"character":57},"end":{"line":322,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":332,"character":57},"end":{"line":332,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":342,"character":57},"end":{"line":342,"character":60}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":354,"character":47},"end":{"line":354,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":358,"character":36},"end":{"line":358,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":363,"character":74},"end":{"line":363,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":522,"character":36},"end":{"line":522,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":542,"character":47},"end":{"line":542,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":548,"character":36},"end":{"line":548,"character":39}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":553,"character":68},"end":{"line":553,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":562,"character":6},"end":{"line":562,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":575,"character":45},"end":{"line":575,"character":48}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":582,"character":6},"end":{"line":582,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":600,"character":47},"end":{"line":600,"character":50}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":607,"character":6},"end":{"line":607,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":621,"character":44},"end":{"line":621,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":627,"character":42},"end":{"line":627,"character":45}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":636,"character":6},"end":{"line":636,"character":9}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":690,"character":44},"end":{"line":690,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":695,"character":34},"end":{"line":695,"character":37}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":713,"character":8},"end":{"line":713,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":729,"character":8},"end":{"line":729,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":740,"character":50},"end":{"line":740,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1104,"character":53},"end":{"line":1104,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":8},"end":{"line":568,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":8},"end":{"line":593,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":8},"end":{"line":613,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":8},"end":{"line":642,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":8},"end":{"line":748,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":776,"character":8},"end":{"line":776,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":845,"character":8},"end":{"line":845,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":875,"character":8},"end":{"line":875,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":909,"character":8},"end":{"line":909,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":922,"character":8},"end":{"line":922,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":931,"character":8},"end":{"line":931,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":954,"character":8},"end":{"line":954,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":969,"character":8},"end":{"line":969,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":981,"character":8},"end":{"line":981,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":993,"character":8},"end":{"line":993,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":996,"character":31},"end":{"line":996,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1023,"character":8},"end":{"line":1023,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":708,"character":8},"end":{"line":708,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":724,"character":8},"end":{"line":724,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1116,"character":13},"end":{"line":1116,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1129,"character":13},"end":{"line":1129,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1135,"character":13},"end":{"line":1135,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1152,"character":8},"end":{"line":1152,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":459,"character":38},"end":{"line":459,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":8},"end":{"line":563,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":608,"character":8},"end":{"line":608,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":637,"character":8},"end":{"line":637,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":10},"end":{"line":714,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":10},"end":{"line":730,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":896,"character":10},"end":{"line":896,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":906,"character":10},"end":{"line":906,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":52},"end":{"line":568,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":568,"character":70},"end":{"line":568,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":50},"end":{"line":593,"character":53}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":593,"character":68},"end":{"line":593,"character":71}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":52},"end":{"line":613,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":613,"character":70},"end":{"line":613,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":54},"end":{"line":642,"character":57}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":642,"character":72},"end":{"line":642,"character":75}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":56},"end":{"line":748,"character":59}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":748,"character":74},"end":{"line":748,"character":77}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":776,"character":59},"end":{"line":776,"character":62}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":845,"character":73},"end":{"line":845,"character":76}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":875,"character":70},"end":{"line":875,"character":73}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":909,"character":61},"end":{"line":909,"character":64}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":922,"character":43},"end":{"line":922,"character":46}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":931,"character":62},"end":{"line":931,"character":65}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":954,"character":71},"end":{"line":954,"character":74}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":969,"character":38},"end":{"line":969,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":981,"character":38},"end":{"line":981,"character":41}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":993,"character":44},"end":{"line":993,"character":47}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":1023,"character":76},"end":{"line":1023,"character":79}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":585,"character":10},"end":{"line":585,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":587,"character":10},"end":{"line":587,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":709,"character":10},"end":{"line":709,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":725,"character":10},"end":{"line":725,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":819,"character":35},"end":{"line":819,"character":38}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":823,"character":12},"end":{"line":823,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":563,"character":53},"end":{"line":563,"character":56}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":584,"character":10},"end":{"line":584,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":608,"character":52},"end":{"line":608,"character":55}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":51},"end":{"line":714,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":714,"character":69},"end":{"line":714,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":51},"end":{"line":730,"character":54}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":730,"character":69},"end":{"line":730,"character":72}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":896,"character":86},"end":{"line":896,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":906,"character":86},"end":{"line":906,"character":89}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":585,"character":94},"end":{"line":585,"character":97}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":587,"character":60},"end":{"line":587,"character":63}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":823,"character":55},"end":{"line":823,"character":58}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":245,"character":60},"end":{"line":245,"character":64}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":245,"character":66},"end":{"line":245,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":245,"character":73},"end":{"line":245,"character":77}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":60},"end":{"line":260,"character":64}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":66},"end":{"line":260,"character":71}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"},{"severity":2,"range":{"start":{"line":260,"character":73},"end":{"line":260,"character":77}},"message":"This function expects a maximum of 1 argument(s) but instead it is receiving 4.","source":"Lua Diagnostics.","code":"redundant-parameter"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/throttling_emitter.lua":[{"severity":2,"range":{"start":{"line":35,"character":4},"end":{"line":35,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":53,"character":4},"end":{"line":53,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/types.lua":[{"severity":2,"range":{"start":{"line":246,"character":28},"end":{"line":246,"character":54}},"message":"Undefined type or alias `vim.api.keyset.set_extmark`.","source":"Lua Diagnostics.","code":"undefined-doc-name"},{"severity":2,"range":{"start":{"line":320,"character":24},"end":{"line":320,"character":38}},"message":"Undefined type or alias `vim.Diagnostic`.","source":"Lua Diagnostics.","code":"undefined-doc-name"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config.lua":[{"severity":2,"range":{"start":{"line":178,"character":11},"end":{"line":178,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":4},"end":{"line":226,"character":7}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":247,"character":13},"end":{"line":247,"character":16}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":200,"character":9},"end":{"line":200,"character":12}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":289,"character":31},"end":{"line":289,"character":34}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":226,"character":98},"end":{"line":226,"character":101}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":305,"character":11},"end":{"line":305,"character":14}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}],"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/footer.lua":[{"severity":2,"range":{"start":{"line":85,"character":2},"end":{"line":85,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":122,"character":2},"end":{"line":122,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":129,"character":2},"end":{"line":129,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":140,"character":2},"end":{"line":140,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":154,"character":2},"end":{"line":154,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":155,"character":2},"end":{"line":155,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":156,"character":2},"end":{"line":156,"character":5}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":49,"character":20},"end":{"line":49,"character":23}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":84,"character":23},"end":{"line":84,"character":26}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":114,"character":8},"end":{"line":114,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":128,"character":21},"end":{"line":128,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":139,"character":21},"end":{"line":139,"character":24}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":62,"character":12},"end":{"line":62,"character":15}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":98,"character":10},"end":{"line":98,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":99,"character":10},"end":{"line":99,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":113,"character":8},"end":{"line":113,"character":11}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"},{"severity":2,"range":{"start":{"line":64,"character":10},"end":{"line":64,"character":13}},"message":"Undefined global `vim`.","source":"Lua Diagnostics.","code":"undefined-global"}]}},"status":"completed","time":{"start":1762176909474,"end":1762176910374},"title":"lua/opencode/core.lua","input":{"newString":" params.model = { modelID = model, providerID = provider }","filePath":"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua","oldString":" params.model = { providerID = provider, modelID = model }"},"output":"\nThis file has errors, please fix\n\n\n\n"},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"reason":"tool-calls","cost":0,"id":"prt_a49edc42b001zynaxIfLCTqFF6","messageID":"msg_a49edb694001syExFxMUW1ik3n","snapshot":"4d76a8382f4cd173216ee6d5949f80aeec2ed395","type":"step-finish","tokens":{"reasoning":0,"cache":{"read":15488,"write":0},"input":15541,"output":237},"sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176906900},"id":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15488,"write":0},"input":15541,"output":237},"providerID":"github-copilot"}}},{"type":"message.part.updated","properties":{"part":{"files":["/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"],"messageID":"msg_a49edb694001syExFxMUW1ik3n","id":"prt_a49edc44d001r66NSVANs6OHqr","type":"patch","hash":"f33f38a70b284207e092c2c578a24e96fdd1bd4d","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176906900,"completed":1762176910414},"id":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15488,"write":0},"input":15541,"output":237},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176906900,"completed":1762176910418},"id":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15488,"write":0},"input":15541,"output":237},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176906900,"completed":1762176910418},"id":"msg_a49edb694001syExFxMUW1ik3n","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15488,"write":0},"input":15541,"output":237},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176910436},"id":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":0,"write":0},"input":0,"output":0},"providerID":"github-copilot"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176910449},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Fixing param-type-not-match and need-check-nil in core.lua line 130","version":"0.15.30"}}},{"type":"message.updated","properties":{"info":{"id":"msg_a49ed91d6001coTsjFq9x6FF5W","time":{"created":1762176897494},"summary":{"diffs":[{"file":"lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":1,"additions":5,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"}]},"role":"user","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_a49edc83d001bg2hfdA1yYp8Q1","snapshot":"4d76a8382f4cd173216ee6d5949f80aeec2ed395","type":"step-start","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176911423},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" type"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID =","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176911423},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" ="}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176911423},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" to"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176911423},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" test"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test suite using the provided script to verify everything is","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176911423},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" is"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test suite using the provided script to verify everything is working.","id":"prt_a49edc83f001YsFxwe2yt9W3Jz","time":{"start":1762176912551,"end":1762176912551},"type":"text","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"callID":"call_4NyLmBcZTmbwsJwuN6gfFqP9","id":"prt_a49edcc8e001xT4R3ZUgjYzQng","tool":"bash","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","state":{"metadata":{"output":"\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\n","description":"Run all tests to verify code changes"},"status":"running","input":{"command":"./run_tests.sh","description":"Run all tests to verify code changes"},"time":{"start":1762176912550}},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"callID":"call_4NyLmBcZTmbwsJwuN6gfFqP9","id":"prt_a49edcc8e001xT4R3ZUgjYzQng","tool":"bash","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","state":{"metadata":{"output":"\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\n","description":"Run all tests to verify code changes"},"status":"running","input":{"command":"./run_tests.sh","description":"Run all tests to verify code changes"},"time":{"start":1762176912721}},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"callID":"call_4NyLmBcZTmbwsJwuN6gfFqP9","id":"prt_a49edcc8e001xT4R3ZUgjYzQng","tool":"bash","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","state":{"metadata":{"exit":0,"description":"Run all tests to verify code changes","output":"\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/unit/keymap_spec.lua\nScheduling: ./tests/unit/opencode_server_spec.lua\nScheduling: ./tests/unit/context_completion_spec.lua\nScheduling: ./tests/unit/config_file_spec.lua\nScheduling: ./tests/unit/init_spec.lua\nScheduling: ./tests/unit/state_spec.lua\nScheduling: ./tests/unit/render_state_spec.lua\nScheduling: ./tests/unit/server_job_spec.lua\nScheduling: ./tests/unit/config_spec.lua\nScheduling: ./tests/unit/context_bar_spec.lua\nScheduling: ./tests/unit/renderer_spec.lua\nScheduling: ./tests/unit/api_client_spec.lua\nScheduling: ./tests/unit/core_spec.lua\nScheduling: ./tests/unit/session_spec.lua\nScheduling: ./tests/unit/event_manager_spec.lua\nScheduling: ./tests/unit/api_spec.lua\nScheduling: ./tests/unit/context_spec.lua\nScheduling: ./tests/unit/timer_spec.lua\nScheduling: ./tests/unit/util_spec.lua\nScheduling: ./tests/unit/snapshot_spec.lua\nScheduling: ./tests/unit/id_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion get_source should return a completion source\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return empty table when trigger char does not match\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return context items when trigger char matches\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include selection items when selections exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include mentioned files when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include subagent items when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should filter items based on input\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should sort items with available items first\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle nil item gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle item without data gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove mentioned file when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove subagent when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove selection when selection item selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should clean up trigger character from current line\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format diagnostics correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format cursor data correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format selection correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle empty context gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle disabled contexts correctly\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with new format configured keys\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server creates a new server object\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_error when stderr is triggered\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup lazily loads config when accessed\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_project returns project\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode has setup function in the public API\t\n\u001b[32mSuccess\u001b[0m\t||\topencode main module can be required without errors\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset creates a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset resets to empty state\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message sets a new message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates line index for message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates existing message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part sets a new part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part updates line index for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part initializes actions array\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns part at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns nil for line without part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns message at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns nil for line without message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id finds part by call ID\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions to part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions with offset\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions clears actions for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets actions at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets all actions from all parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines updates part line positions\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when expanding\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part removes part and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part clears line index for removed part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message removes message and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message clears line index for removed message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message returns false for non-existent message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does nothing when delta is 0\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts content at or after from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts actions with parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does not rebuild index when nothing shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all invalidates index when content shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all exits early when content found before from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data updates part reference\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data does nothing for non-existent part\t\n\t\n\u001b[32mSuccess: \u001b[0m\t37\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job exposes expected public functions\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api rejects on non 2xx\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job stream_api forwards chunks\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job ensure_server spawns a new opencode server only once\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config uses default values when no options are provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config merges user options with defaults\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar setup subscribes to state changes\t\n\t\n\u001b[32mSuccess: \u001b[0m\t9\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create a new client instance\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should remove trailing slash from base_url\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create client using create factory function\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should have all expected API methods\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should construct URLs correctly with query parameters\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nError detected while processing command line:\nopencode command not found - please install and configure opencode before using this plugin\nUnsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open creates windows if they don't exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open handles new session properly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open focuses the appropriate window\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core select_session filters sessions by description and parentID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message sends a message via api_client\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message creates new session when none active\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message persist options in state when sending message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles empty session list\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns the session with matching ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns nil when no session matches the ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil for non-existent directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns decoded JSON content from directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir skips invalid JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when session is nil\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when messages directory does not exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns messages with their parts\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies listeners on key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies wildcard listeners on any key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) can unregister listeners\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) does not notify if value is unchanged\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should create a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should subscribe and emit events\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle multiple subscribers\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should unsubscribe correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should track subscriber count\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should list event names\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle starting and stopping\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should not start multiple times\t\n\t\n\u001b[32mSuccess: \u001b[0m\t8\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nFile not added to context. Could not read.\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message returns nils if message or parts missing\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_message_legacy extracts legacy tags from text\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag extracts content between tags\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag returns nil if tag not found\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message returns a parts array with prompt as first part\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message includes mentioned_files and subagents\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes current_file if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes mentioned_subagents if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a selection\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a subagent\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n:OpencodeOpenInput is deprecated. Use `:Opencode open input` instead\n:OpencodeStop is deprecated. Use `:Opencode cancel` instead\n:OpencodeClose is deprecated. Use `:Opencode close` instead\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api commands table contains the expected commands with proper structure\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup registers the main Opencode command and legacy commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup sets up legacy command functions that route to main command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api Lua API provides callable functions that match commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing works with run_new command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing requires a prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses flat booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses nested dot notation\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses mixed nesting and booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses numbers\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses no prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single agent prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single model prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single context prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses multiple prefixes in order\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args stops parsing at first non-prefix token\t\n\t\n\u001b[32mSuccess: \u001b[0m\t14\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if no active session\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if read-tree fails\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if checkout-index fails\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate ascending session IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate descending message IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate given IDs correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should throw error for invalid given IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate schemas correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should return available prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate IDs with correct length structure\t\n\t\n\u001b[32mSuccess: \u001b[0m\t7\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new creates a new timer with required options\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new stores optional parameters\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a repeating timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a one-shot timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start passes arguments to on_tick function\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick returns false\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick throws an error\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops previous timer before starting new one\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start throws error when timer creation fails\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop stops a running timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop calls on_stop callback when provided\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop does nothing when timer is not running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false when timer is not started\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns true when timer is running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after timer is stopped\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after one-shot timer completes\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests can restart a stopped timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests handles rapid start/stop cycles\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (session)\t\n\t\n\u001b[32mSuccess: \u001b[0m\t60\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Unit tests passed\u001b[0m\n------------------------------------------------\n\u001b[0;32mAll tests passed successfully!\u001b[0m\n"},"status":"completed","time":{"start":1762176912541,"end":1762176915509},"title":"./run_tests.sh","input":{"command":"./run_tests.sh","description":"Run all tests to verify code changes"},"output":"\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/unit/keymap_spec.lua\nScheduling: ./tests/unit/opencode_server_spec.lua\nScheduling: ./tests/unit/context_completion_spec.lua\nScheduling: ./tests/unit/config_file_spec.lua\nScheduling: ./tests/unit/init_spec.lua\nScheduling: ./tests/unit/state_spec.lua\nScheduling: ./tests/unit/render_state_spec.lua\nScheduling: ./tests/unit/server_job_spec.lua\nScheduling: ./tests/unit/config_spec.lua\nScheduling: ./tests/unit/context_bar_spec.lua\nScheduling: ./tests/unit/renderer_spec.lua\nScheduling: ./tests/unit/api_client_spec.lua\nScheduling: ./tests/unit/core_spec.lua\nScheduling: ./tests/unit/session_spec.lua\nScheduling: ./tests/unit/event_manager_spec.lua\nScheduling: ./tests/unit/api_spec.lua\nScheduling: ./tests/unit/context_spec.lua\nScheduling: ./tests/unit/timer_spec.lua\nScheduling: ./tests/unit/util_spec.lua\nScheduling: ./tests/unit/snapshot_spec.lua\nScheduling: ./tests/unit/id_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion get_source should return a completion source\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return empty table when trigger char does not match\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return context items when trigger char matches\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include selection items when selections exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include mentioned files when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include subagent items when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should filter items based on input\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should sort items with available items first\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle nil item gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle item without data gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove mentioned file when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove subagent when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove selection when selection item selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should clean up trigger character from current line\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format diagnostics correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format cursor data correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format selection correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle empty context gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle disabled contexts correctly\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with new format configured keys\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server creates a new server object\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_error when stderr is triggered\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup lazily loads config when accessed\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_project returns project\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode has setup function in the public API\t\n\u001b[32mSuccess\u001b[0m\t||\topencode main module can be required without errors\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset creates a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset resets to empty state\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message sets a new message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates line index for message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates existing message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part sets a new part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part updates line index for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part initializes actions array\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns part at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns nil for line without part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns message at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns nil for line without message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id finds part by call ID\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions to part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions with offset\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions clears actions for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets actions at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets all actions from all parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines updates part line positions\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when expanding\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part removes part and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part clears line index for removed part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message removes message and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message clears line index for removed message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message returns false for non-existent message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does nothing when delta is 0\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts content at or after from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts actions with parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does not rebuild index when nothing shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all invalidates index when content shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all exits early when content found before from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data updates part reference\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data does nothing for non-existent part\t\n\t\n\u001b[32mSuccess: \u001b[0m\t37\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job exposes expected public functions\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api rejects on non 2xx\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job stream_api forwards chunks\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job ensure_server spawns a new opencode server only once\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config uses default values when no options are provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config merges user options with defaults\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar setup subscribes to state changes\t\n\t\n\u001b[32mSuccess: \u001b[0m\t9\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create a new client instance\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should remove trailing slash from base_url\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create client using create factory function\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should have all expected API methods\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should construct URLs correctly with query parameters\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nError detected while processing command line:\nopencode command not found - please install and configure opencode before using this plugin\nUnsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open creates windows if they don't exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open handles new session properly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open focuses the appropriate window\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core select_session filters sessions by description and parentID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message sends a message via api_client\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message creates new session when none active\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message persist options in state when sending message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles empty session list\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns the session with matching ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns nil when no session matches the ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil for non-existent directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns decoded JSON content from directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir skips invalid JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when session is nil\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when messages directory does not exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns messages with their parts\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies listeners on key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies wildcard listeners on any key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) can unregister listeners\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) does not notify if value is unchanged\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should create a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should subscribe and emit events\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle multiple subscribers\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should unsubscribe correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should track subscriber count\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should list event names\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle starting and stopping\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should not start multiple times\t\n\t\n\u001b[32mSuccess: \u001b[0m\t8\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nFile not added to context. Could not read.\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message returns nils if message or parts missing\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_message_legacy extracts legacy tags from text\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag extracts content between tags\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag returns nil if tag not found\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message returns a parts array with prompt as first part\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message includes mentioned_files and subagents\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes current_file if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes mentioned_subagents if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a selection\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a subagent\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n:OpencodeOpenInput is deprecated. Use `:Opencode open input` instead\n:OpencodeStop is deprecated. Use `:Opencode cancel` instead\n:OpencodeClose is deprecated. Use `:Opencode close` instead\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api commands table contains the expected commands with proper structure\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup registers the main Opencode command and legacy commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup sets up legacy command functions that route to main command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api Lua API provides callable functions that match commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing works with run_new command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing requires a prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses flat booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses nested dot notation\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses mixed nesting and booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses numbers\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses no prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single agent prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single model prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single context prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses multiple prefixes in order\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args stops parsing at first non-prefix token\t\n\t\n\u001b[32mSuccess: \u001b[0m\t14\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if no active session\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if read-tree fails\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if checkout-index fails\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate ascending session IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate descending message IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate given IDs correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should throw error for invalid given IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate schemas correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should return available prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate IDs with correct length structure\t\n\t\n\u001b[32mSuccess: \u001b[0m\t7\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new creates a new timer with required options\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new stores optional parameters\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a repeating timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a one-shot timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start passes arguments to on_tick function\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick returns false\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick throws an error\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops previous timer before starting new one\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start throws error when timer creation fails\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop stops a running timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop calls on_stop callback when provided\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop does nothing when timer is not running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false when timer is not started\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns true when timer is running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after timer is stopped\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after one-shot timer completes\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests can restart a stopped timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests handles rapid start/stop cycles\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (session)\t\n\t\n\u001b[32mSuccess: \u001b[0m\t60\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Unit tests passed\u001b[0m\n------------------------------------------------\n\u001b[0;32mAll tests passed successfully!\u001b[0m\n"},"type":"tool","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"reason":"tool-calls","cost":0,"id":"prt_a49edd837001lGPaOMUY99F0JS","messageID":"msg_a49edc4640017DZPFVUPHCm7Ji","snapshot":"4d76a8382f4cd173216ee6d5949f80aeec2ed395","type":"step-finish","tokens":{"reasoning":0,"cache":{"read":7680,"write":0},"input":15805,"output":102},"sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176910436},"id":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":7680,"write":0},"input":15805,"output":102},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176910436,"completed":1762176915544},"id":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":7680,"write":0},"input":15805,"output":102},"providerID":"github-copilot"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176910436,"completed":1762176915546},"id":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":7680,"write":0},"input":15805,"output":102},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176910436,"completed":1762176915547},"id":"msg_a49edc4640017DZPFVUPHCm7Ji","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":7680,"write":0},"input":15805,"output":102},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176915583},"id":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":0,"write":0},"input":0,"output":0},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"id":"msg_a49ed91d6001coTsjFq9x6FF5W","time":{"created":1762176897494},"summary":{"diffs":[{"file":"lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":1,"additions":5,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"}]},"role":"user","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176915602},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Fixing param-type-not-match and need-check-nil in core.lua line 130","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_a49eddd62001iPYFPbJ6Y4rnHk","snapshot":"4d76a8382f4cd173216ee6d5949f80aeec2ed395","type":"step-start","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" of"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" for"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":"M"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" `"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ...,","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" ...,"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" the"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" now"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" line"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176916836},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"},"delta":" want"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"text":"All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!","id":"prt_a49eddd640016qrxxmOdhzVlPS","time":{"start":1762176918065,"end":1762176918065},"type":"text","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.part.updated","properties":{"part":{"reason":"stop","cost":0,"id":"prt_a49ede231001JoNcJuwk65yXP6","messageID":"msg_a49edd87f001eOJDDoq5Vb5hra","snapshot":"4d76a8382f4cd173216ee6d5949f80aeec2ed395","type":"step-finish","tokens":{"reasoning":0,"cache":{"read":15744,"write":0},"input":24075,"output":120},"sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176915583},"id":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15744,"write":0},"input":24075,"output":120},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176915583,"completed":1762176918091},"id":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15744,"write":0},"input":24075,"output":120},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176915583,"completed":1762176918092},"id":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15744,"write":0},"input":24075,"output":120},"providerID":"github-copilot"}}},{"type":"message.updated","properties":{"info":{"mode":"build","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_a49ed91d6001coTsjFq9x6FF5W","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"cost":0,"modelID":"gpt-4.1","time":{"created":1762176915583,"completed":1762176918093},"id":"msg_a49edd87f001eOJDDoq5Vb5hra","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7","tokens":{"reasoning":0,"cache":{"read":15744,"write":0},"input":24075,"output":120},"providerID":"github-copilot"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_a49ed91d6001coTsjFq9x6FF5W","time":{"created":1762176897494},"summary":{"diffs":[{"file":"lua/opencode/core.lua","before":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n","deletions":1,"additions":5,"after":"-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n"}]},"role":"user","sessionID":"ses_5b612cfbefferUqNCDHOrQunq7"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5b612cfbefferUqNCDHOrQunq7","time":{"created":1762176872514,"updated":1762176918135},"summary":{"diffs":[]},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Fixing param-type-not-match and need-check-nil in core.lua line 130","version":"0.15.30"}}},{"type":"custom.emit_events.finished","properties":[]}] +[ + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176872514 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2025-11-03T13:34:32.514Z", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "role": "user", + "time": { "created": 1762176897494 } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "help me fix the file", + "id": "prt_a49ed91d70014tkQ4g5HLMkbpH", + "type": "text", + "messageID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\"}", + "id": "prt_a49ed91db0014Vn7x8yXZAkOxO", + "synthetic": true, + "type": "text", + "messageID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "\n00001| -- This file was written by an automated tool.\n00002| local state = require('opencode.state')\n00003| local context = require('opencode.context')\n00004| local session = require('opencode.session')\n00005| local ui = require('opencode.ui.ui')\n00006| local server_job = require('opencode.server_job')\n00007| local input_window = require('opencode.ui.input_window')\n00008| local util = require('opencode.util')\n00009| local config = require('opencode.config')\n00010| \n00011| local M = {}\n00012| M._abort_count = 0\n00013| \n00014| ---@param parent_id string?\n00015| function M.select_session(parent_id)\n00016| local all_sessions = session.get_all_workspace_sessions() or {}\n00017| local filtered_sessions = vim.tbl_filter(function(s)\n00018| return s.description ~= '' and s ~= nil and s.parentID == parent_id\n00019| end, all_sessions)\n00020| \n00021| ui.select_session(filtered_sessions, function(selected_session)\n00022| if not selected_session then\n00023| if state.windows then\n00024| ui.focus_input()\n00025| end\n00026| return\n00027| end\n00028| state.active_session = selected_session\n00029| if state.windows then\n00030| state.restore_points = {}\n00031| -- Don't need to update either renderer because they subscribe to\n00032| -- session changes\n00033| ui.focus_input()\n00034| else\n00035| M.open()\n00036| end\n00037| end)\n00038| end\n00039| \n00040| ---@param opts? OpenOpts\n00041| function M.open(opts)\n00042| opts = opts or { focus = 'input', new_session = false }\n00043| \n00044| if not state.opencode_server or not state.opencode_server:is_running() then\n00045| state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n00046| end\n00047| \n00048| M.ensure_current_mode()\n00049| \n00050| local are_windows_closed = state.windows == nil\n00051| \n00052| if not require('opencode.ui.ui').is_opencode_focused() then\n00053| require('opencode.context').load()\n00054| end\n00055| \n00056| if are_windows_closed then\n00057| -- Check if whether prompting will be allowed\n00058| local context_module = require('opencode.context')\n00059| local mentioned_files = context_module.context.mentioned_files or {}\n00060| local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n00061| if not allowed then\n00062| vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n00063| end\n00064| \n00065| state.windows = ui.create_windows()\n00066| end\n00067| \n00068| if opts.new_session then\n00069| state.active_session = nil\n00070| state.last_sent_context = nil\n00071| state.active_session = M.create_new_session()\n00072| else\n00073| if not state.active_session then\n00074| state.active_session = session.get_last_workspace_session()\n00075| else\n00076| if not state.display_route and are_windows_closed then\n00077| -- We're not displaying /help or something like that but we have an active session\n00078| -- and the windows were closed so we need to do a full refresh. This mostly happens\n00079| -- when opening the window after having closed it since we're not currently clearing\n00080| -- the session on api.close()\n00081| ui.render_output(false)\n00082| end\n00083| end\n00084| end\n00085| \n00086| if opts.focus == 'input' then\n00087| ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n00088| elseif opts.focus == 'output' then\n00089| ui.focus_output({ restore_position = are_windows_closed })\n00090| end\n00091| state.is_opencode_focused = true\n00092| end\n00093| \n00094| --- Sends a message to the active session, creating one if necessary.\n00095| --- @param prompt string The message prompt to send.\n00096| --- @param opts? SendMessageOpts\n00097| function M.send_message(prompt, opts)\n00098| local mentioned_files = context.context.mentioned_files or {}\n00099| local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n00100| \n00101| if not allowed then\n00102| vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n00103| return\n00104| end\n00105| \n00106| opts = opts or {}\n00107| \n00108| opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n00109| state.current_context_config = opts.context\n00110| context.load()\n00111| opts.model = opts.model or state.current_model\n00112| opts.agent = opts.agent or state.current_mode or config.default_mode\n00113| \n00114| local params = {}\n00115| \n00116| if opts.model then\n00117| local provider, model = opts.model:match('^(.-)/(.+)$')\n00118| params.model = { providerID = provider, modelID = model }\n00119| state.current_model = opts.model\n00120| end\n00121| \n00122| if opts.agent then\n00123| params.agent = opts.agent\n00124| state.current_mode = opts.agent\n00125| end\n00126| \n00127| params.parts = context.format_message(prompt, opts.context)\n00128| M.before_run(opts)\n00129| \n00130| state.api_client\n00131| :create_message(state.active_session.id, params)\n00132| :and_then(function(response)\n00133| if not response or not response.info or not response.parts then\n00134| -- fall back to full render. incremental render is handled\n00135| -- event manager\n00136| ui.render_output()\n00137| end\n00138| \n00139| M.after_run(prompt)\n00140| end)\n00141| :catch(function(err)\n00142| vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n00143| M.cancel()\n00144| end)\n00145| end\n00146| \n00147| ---@param title? string\n00148| ---@return Session?\n00149| function M.create_new_session(title)\n00150| local session_response = state.api_client\n00151| :create_session(title and { title = title } or false)\n00152| :catch(function(err)\n00153| vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n00154| end)\n00155| :wait()\n00156| \n00157| if session_response and session_response.id then\n00158| local new_session = session.get_by_id(session_response.id)\n00159| return new_session\n00160| end\n00161| end\n00162| \n00163| ---@param prompt string\n00164| function M.after_run(prompt)\n00165| context.unload_attachments()\n00166| state.last_sent_context = vim.deepcopy(context.context)\n00167| require('opencode.history').write(prompt)\n00168| M._abort_count = 0\n00169| end\n00170| \n00171| ---@param opts? SendMessageOpts\n00172| function M.before_run(opts)\n00173| local is_new_session = opts and opts.new_session or not state.active_session\n00174| opts = opts or {}\n00175| \n00176| M.cancel()\n00177| -- ui.clear_output()\n00178| \n00179| M.open({\n00180| new_session = is_new_session,\n00181| })\n00182| end\n00183| \n00184| function M.configure_provider()\n00185| require('opencode.provider').select(function(selection)\n00186| if not selection then\n00187| if state.windows then\n00188| ui.focus_input()\n00189| end\n00190| return\n00191| end\n00192| local model_str = string.format('%s/%s', selection.provider, selection.model)\n00193| state.current_model = model_str\n00194| \n00195| if state.windows then\n00196| require('opencode.ui.topbar').render()\n00197| ui.focus_input()\n00198| else\n00199| vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n00200| end\n00201| end)\n00202| end\n00203| \n00204| function M.cancel()\n00205| if state.windows and state.active_session then\n00206| if state.is_running() then\n00207| M._abort_count = M._abort_count + 1\n00208| \n00209| -- if there's a current permission, reject it\n00210| if state.current_permission then\n00211| require('opencode.api').permission_deny()\n00212| end\n00213| \n00214| local ok, result = pcall(function()\n00215| return state.api_client:abort_session(state.active_session.id):wait()\n00216| end)\n00217| \n00218| if not ok then\n00219| vim.notify('Abort error: ' .. vim.inspect(result))\n00220| end\n00221| \n00222| if M._abort_count >= 3 then\n00223| vim.notify('Re-starting Opencode server')\n00224| M._abort_count = 0\n00225| -- close existing server\n00226| if state.opencode_server then\n00227| state.opencode_server:shutdown():wait()\n00228| end\n00229| \n00230| -- start a new one\n00231| state.opencode_server = nil\n00232| \n00233| -- NOTE: start a new server here to make sure we're subscribed\n00234| -- to server events before a user sends a message\n00235| state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n00236| end\n00237| end\n00238| require('opencode.ui.footer').clear()\n00239| input_window.set_content('')\n00240| require('opencode.history').index = nil\n00241| ui.focus_input()\n00242| end\n00243| end\n00244| \n00245| function M.opencode_ok()\n00246| if vim.fn.executable('opencode') == 0 then\n00247| vim.notify(\n00248| 'opencode command not found - please install and configure opencode before using this plugin',\n00249| vim.log.levels.ERROR\n00250| )\n00251| return false\n00252| end\n00253| \n00254| if not state.opencode_cli_version or state.opencode_cli_version == '' then\n00255| local result = vim.system({ 'opencode', '--version' }):wait()\n00256| local out = (result and result.stdout or ''):gsub('%s+$', '')\n00257| state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n00258| end\n00259| \n00260| local required = state.required_version\n00261| local current_version = state.opencode_cli_version\n00262| \n00263| if not current_version or current_version == '' then\n00264| vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n00265| return false\n00266| end\n00267| \n00268| if not util.is_version_greater_or_equal(current_version, required) then\n00269| vim.notify(\n00270| string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n00271| vim.log.levels.ERROR\n00272| )\n00273| return false\n00274| end\n00275| \n00276| return true\n00277| end\n00278| \n00279| local function on_opencode_server()\n00280| state.current_permission = nil\n00281| end\n00282| \n00283| --- Switches the current mode to the specified agent.\n00284| --- @param mode string The agent/mode to switch to\n00285| --- @return boolean success Returns true if the mode was switched successfully, false otherwise\n00286| function M.switch_to_mode(mode)\n00287| if not mode or mode == '' then\n00288| vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n00289| return false\n00290| end\n00291| \n00292| local config_file = require('opencode.config_file')\n00293| local available_agents = config_file.get_opencode_agents()\n00294| \n00295| if not vim.tbl_contains(available_agents, mode) then\n00296| vim.notify(\n00297| string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n00298| vim.log.levels.ERROR\n00299| )\n00300| return false\n00301| end\n00302| \n00303| state.current_mode = mode\n00304| ui.render_output()\n00305| return true\n00306| end\n00307| \n00308| --- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n00309| --- @return boolean success Returns true if current_mode is set\n00310| function M.ensure_current_mode()\n00311| if state.current_mode == nil then\n00312| local config_file = require('opencode.config_file')\n00313| local available_agents = config_file.get_opencode_agents()\n00314| \n00315| if not available_agents or #available_agents == 0 then\n00316| vim.notify('No available agents found', vim.log.levels.ERROR)\n00317| return false\n00318| end\n00319| \n00320| local default_mode = config.default_mode\n00321| \n00322| -- Try to use the configured default mode if it's available\n00323| if default_mode and vim.tbl_contains(available_agents, default_mode) then\n00324| state.current_mode = default_mode\n00325| else\n00326| -- Fallback to first available agent\n00327| state.current_mode = available_agents[1]\n00328| end\n00329| end\n00330| return true\n00331| end\n00332| \n00333| function M.setup()\n00334| state.subscribe('opencode_server', on_opencode_server)\n00335| \n00336| vim.schedule(function()\n00337| M.opencode_ok()\n00338| end)\n00339| local OpencodeApiClient = require('opencode.api_client')\n00340| state.api_client = OpencodeApiClient.create()\n00341| end\n00342| \n00343| return M\n00344| \n", + "id": "prt_a49ed91db002IGcvEBKqHjLjcH", + "synthetic": true, + "type": "text", + "messageID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "id": "prt_a49ed91db00317OxenmCorS0sB", + "messageID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "mime": "text/plain", + "type": "file", + "filename": "lua/opencode/core.lua" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "{\"content\":[{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"param-type-not-match\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":20},\"end\":{\"line\":130,\"character\":43}},\"message\":\"expected `string` but found `string?`. \",\"source\":\"EmmyLua\",\"code\":\"param-type-not-match\"}},\"namespace\":103,\"_extmark_id\":1,\"end_col\":43,\"message\":\"expected `string` but found `string?`. \",\"source\":\"EmmyLua\",\"col\":20},{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"param-type-not-match\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":45},\"end\":{\"line\":130,\"character\":51}},\"message\":\"expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`. member model not match, expect { modelID: string, providerID: string }?, but got { providerID = string?, modelID = string? }\",\"source\":\"EmmyLua\",\"code\":\"param-type-not-match\"}},\"namespace\":103,\"_extmark_id\":2,\"end_col\":51,\"message\":\"expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`. member model not match, expect { modelID: string, providerID: string }?, but got { providerID = string?, modelID = string? }\",\"source\":\"EmmyLua\",\"col\":45},{\"lnum\":130,\"end_lnum\":130,\"bufnr\":13,\"code\":\"need-check-nil\",\"severity\":2,\"user_data\":{\"lsp\":{\"severity\":2,\"range\":{\"start\":{\"line\":130,\"character\":20},\"end\":{\"line\":130,\"character\":40}},\"message\":\"state.active_session may be nil\",\"source\":\"EmmyLua\",\"code\":\"need-check-nil\"}},\"namespace\":103,\"_extmark_id\":3,\"end_col\":40,\"message\":\"state.active_session may be nil\",\"source\":\"EmmyLua\",\"col\":20}],\"context_type\":\"diagnostics\"}", + "id": "prt_a49ed91d70021vvuvYgF7OgY5D", + "synthetic": true, + "type": "text", + "messageID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176897510 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2025-11-03T13:34:32.514Z", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176899112 }, + "id": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 0, "write": 0 }, "input": 0, "output": 0 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "time": { "created": 1762176897494 }, + "summary": { "diffs": [] }, + "role": "user", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176899140 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2025-11-03T13:34:32.514Z", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_a49ed9b590016OgTFP0GBP3nPs", + "snapshot": "8e7903714919009004aad8754db0035fb47ecb24", + "type": "step-start", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "130" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176900011 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Fixing param-type-not-match and need-check-nil in core.lua line 130", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "-match" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " **" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130,", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "," + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "nil" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " be" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "string" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " nullable" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session`", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "`" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "-not" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID:", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": ":" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " tools" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " parts" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts =", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " =" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "`\n" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " (" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string?", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "?" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string,", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "," + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, provider", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " provider" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "ity" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " matches" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `{" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "-check" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " **" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " check" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " nil" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "---\n\n" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] ", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " " + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `." + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " expected" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, provider", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " provider" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " diagnostics" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " resolved" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\n", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "`\n\n" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session`", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "`" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176899931 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "`." + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_ABJ98MrScaXDP17UCnRACu6s", + "id": "prt_a49edb4c4001QGmKwYoe4BcZ9j", + "tool": "edit", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "state": { + "status": "running", + "input": { + "newString": " if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "oldString": " state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)" + }, + "time": { "start": 1762176906475 } + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "file.edited", + "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua" } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Let's address the three EmmyLua diagnostics at line 130 in `lua/opencode/core.lua`:\n\n### 1. `param-type-not-match`: expected `string` but found `string?`\n- **Location:** `state.active_session.id` (line 130, col 20)\n- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.\n- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.\n\n### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`\n- **Location:** `params` argument to `:create_message` (line 130, col 45)\n- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.\n- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.\n\n### 3. `need-check-nil`: `state.active_session may be nil`\n- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.\n\n---\n\n## Step-by-step Fix Plan\n\n```markdown\n- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.\n```\n\nLet's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.", + "id": "prt_a49ed9b5b001nHdvHRhJEv3ZRU", + "time": { "start": 1762176906487, "end": 1762176906487 }, + "type": "text", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_ABJ98MrScaXDP17UCnRACu6s", + "id": "prt_a49edb4c4001QGmKwYoe4BcZ9j", + "tool": "edit", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "state": { + "metadata": { + "filediff": { + "file": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 0, + "additions": 4, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + }, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n+++ /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n@@ -126,8 +126,12 @@\n \n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n \n+if not state.active_session or not state.active_session.id then\n+ vim.notify('No active session to send message to', vim.log.levels.ERROR)\n+ return\n+end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n", + "diagnostics": { + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/autocmds.lua": [ + { + "severity": 2, + "range": { "start": { "line": 11, "character": 2 }, "end": { "line": 11, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 2 }, "end": { "line": 37, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 2 }, "end": { "line": 48, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 2 }, "end": { "line": 58, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 16 }, "end": { "line": 5, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 23 }, "end": { "line": 47, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 9 }, "end": { "line": 16, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 8 }, "end": { "line": 17, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 44 }, "end": { "line": 32, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 31 }, "end": { "line": 33, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 23 }, "end": { "line": 66, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 24 }, "end": { "line": 62, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 59 }, "end": { "line": 35, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 6 }, "end": { "line": 90, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 6 }, "end": { "line": 93, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/commands.lua": [ + { + "severity": 2, + "range": { "start": { "line": 81, "character": 8 }, "end": { "line": 81, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 37 }, "end": { "line": 27, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 10 }, "end": { "line": 75, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 56 }, "end": { "line": 81, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config_file.lua": [ + { + "severity": 2, + "range": { "start": { "line": 17, "character": 4 }, "end": { "line": 17, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 4 }, "end": { "line": 34, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 14 }, "end": { "line": 61, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 11 }, "end": { "line": 90, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 53 }, "end": { "line": 17, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 74 }, "end": { "line": 17, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 54 }, "end": { "line": 34, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 75 }, "end": { "line": 34, "character": 78 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 56 }, "end": { "line": 51, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 77 }, "end": { "line": 51, "character": 80 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/blink_cmp.lua": [ + { + "severity": 2, + "range": { "start": { "line": 24, "character": 9 }, "end": { "line": 24, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 37 }, "end": { "line": 35, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 49 }, "end": { "line": 35, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/nvim_cmp.lua": [ + { + "severity": 2, + "range": { "start": { "line": 83, "character": 18 }, "end": { "line": 83, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 11 }, "end": { "line": 26, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 14 }, "end": { "line": 86, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 39 }, "end": { "line": 34, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 51 }, "end": { "line": 34, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/vim_complete.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 2 }, "end": { "line": 25, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 116, "character": 25 }, "end": { "line": 116, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 21 }, "end": { "line": 20, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 15 }, "end": { "line": 46, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 14 }, "end": { "line": 47, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 15 }, "end": { "line": 64, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 14 }, "end": { "line": 65, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 37 }, "end": { "line": 38, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 9 }, "end": { "line": 85, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 49 }, "end": { "line": 38, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 6 }, "end": { "line": 108, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 6 }, "end": { "line": 9, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 6 }, "end": { "line": 10, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 41 }, "end": { "line": 106, "character": 44 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua": [ + { + "severity": 2, + "range": { "start": { "line": 286, "character": 2 }, "end": { "line": 286, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 290, "character": 2 }, "end": { "line": 290, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 2 }, "end": { "line": 292, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 297, "character": 2 }, "end": { "line": 297, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 296, "character": 2 }, "end": { "line": 296, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 2 }, "end": { "line": 298, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 2 }, "end": { "line": 299, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 504, "character": 2 }, "end": { "line": 504, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 516, "character": 2 }, "end": { "line": 516, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 21 }, "end": { "line": 58, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 27 }, "end": { "line": 59, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 23 }, "end": { "line": 73, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 22 }, "end": { "line": 78, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 26 }, "end": { "line": 79, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 4 }, "end": { "line": 144, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 18 }, "end": { "line": 218, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 25 }, "end": { "line": 266, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 479, "character": 18 }, "end": { "line": 479, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 45 }, "end": { "line": 28, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 29 }, "end": { "line": 51, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 22 }, "end": { "line": 92, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 115, "character": 29 }, "end": { "line": 115, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 29 }, "end": { "line": 129, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 18 }, "end": { "line": 141, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 142, "character": 17 }, "end": { "line": 142, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 9 }, "end": { "line": 148, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 9 }, "end": { "line": 150, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 9 }, "end": { "line": 156, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 167, "character": 29 }, "end": { "line": 167, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 9 }, "end": { "line": 179, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 182, "character": 29 }, "end": { "line": 182, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 196, "character": 29 }, "end": { "line": 196, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 8 }, "end": { "line": 237, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 15 }, "end": { "line": 249, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 21 }, "end": { "line": 265, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 22 }, "end": { "line": 281, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 282, "character": 18 }, "end": { "line": 282, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 22 }, "end": { "line": 283, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 15 }, "end": { "line": 287, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 21 }, "end": { "line": 291, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 293, "character": 19 }, "end": { "line": 293, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 24 }, "end": { "line": 298, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 312, "character": 19 }, "end": { "line": 312, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 479, "character": 51 }, "end": { "line": 479, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 483, "character": 11 }, "end": { "line": 483, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 492, "character": 11 }, "end": { "line": 492, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 503, "character": 18 }, "end": { "line": 503, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 11 }, "end": { "line": 255, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 256, "character": 16 }, "end": { "line": 256, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 334, "character": 11 }, "end": { "line": 334, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 347, "character": 11 }, "end": { "line": 347, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 357, "character": 11 }, "end": { "line": 357, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 423, "character": 27 }, "end": { "line": 423, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 60 }, "end": { "line": 30, "character": 63 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 34 }, "end": { "line": 83, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 34 }, "end": { "line": 86, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 34 }, "end": { "line": 89, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 31 }, "end": { "line": 250, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 42 }, "end": { "line": 354, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 9 }, "end": { "line": 276, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 34 }, "end": { "line": 266, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 509, "character": 49 }, "end": { "line": 509, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 521, "character": 49 }, "end": { "line": 521, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 38 }, "end": { "line": 141, "character": 42 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 64 }, "end": { "line": 298, "character": 68 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 70 }, "end": { "line": 298, "character": 75 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 77 }, "end": { "line": 298, "character": 81 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 51 }, "end": { "line": 250, "character": 55 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/sort.lua": [ + { + "severity": 2, + "range": { "start": { "line": 31, "character": 40 }, "end": { "line": 31, "character": 43 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 40 }, "end": { "line": 32, "character": 43 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/context_bar.lua": [ + { + "severity": 2, + "range": { "start": { "line": 172, "character": 2 }, "end": { "line": 172, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 2 }, "end": { "line": 162, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 16 }, "end": { "line": 149, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 10 }, "end": { "line": 151, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 18 }, "end": { "line": 148, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 9 }, "end": { "line": 155, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 4 }, "end": { "line": 180, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 7 }, "end": { "line": 98, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 7 }, "end": { "line": 99, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 7 }, "end": { "line": 100, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 20 }, "end": { "line": 175, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/contextual_actions.lua": [ + { + "severity": 2, + "range": { "start": { "line": 22, "character": 2 }, "end": { "line": 22, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 2 }, "end": { "line": 42, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 4 }, "end": { "line": 82, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 16 }, "end": { "line": 19, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 18 }, "end": { "line": 20, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 6 }, "end": { "line": 88, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 6 }, "end": { "line": 26, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 6 }, "end": { "line": 46, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 6 }, "end": { "line": 63, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 8 }, "end": { "line": 31, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 8 }, "end": { "line": 54, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 10 }, "end": { "line": 90, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 25 }, "end": { "line": 27, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 25 }, "end": { "line": 50, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 93, "character": 27 }, "end": { "line": 93, "character": 33 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/debug_helper.lua": [ + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 2 }, "end": { "line": 77, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 2 }, "end": { "line": 16, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 2 }, "end": { "line": 76, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 19 }, "end": { "line": 16, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 4 }, "end": { "line": 20, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 4 }, "end": { "line": 32, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 4 }, "end": { "line": 64, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 4 }, "end": { "line": 70, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 16 }, "end": { "line": 75, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 18 }, "end": { "line": 11, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 19 }, "end": { "line": 12, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 23 }, "end": { "line": 35, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 51 }, "end": { "line": 46, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 19 }, "end": { "line": 74, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 72 }, "end": { "line": 77, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 5 }, "end": { "line": 18, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 46 }, "end": { "line": 32, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 48 }, "end": { "line": 64, "character": 51 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 45 }, "end": { "line": 70, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/diff_tab.lua": [ + { + "severity": 2, + "range": { "start": { "line": 37, "character": 2 }, "end": { "line": 37, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 2 }, "end": { "line": 56, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 24 }, "end": { "line": 35, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 4 }, "end": { "line": 43, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 4 }, "end": { "line": 44, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 10 }, "end": { "line": 14, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 27 }, "end": { "line": 36, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 17 }, "end": { "line": 38, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 18 }, "end": { "line": 54, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 22 }, "end": { "line": 55, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 22 }, "end": { "line": 5, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 10 }, "end": { "line": 6, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 6 }, "end": { "line": 47, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 6 }, "end": { "line": 48, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 20 }, "end": { "line": 7, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 18 }, "end": { "line": 10, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 9 }, "end": { "line": 16, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 50 }, "end": { "line": 35, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 23 }, "end": { "line": 40, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 25 }, "end": { "line": 44, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 23 }, "end": { "line": 51, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 16 }, "end": { "line": 25, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 29 }, "end": { "line": 18, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 13 }, "end": { "line": 19, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/file_picker.lua": [ + { + "severity": 2, + "range": { "start": { "line": 157, "character": 2 }, "end": { "line": 157, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 21 }, "end": { "line": 103, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 22 }, "end": { "line": 104, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 21 }, "end": { "line": 105, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 11 }, "end": { "line": 11, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 6 }, "end": { "line": 130, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 6 }, "end": { "line": 134, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 8 }, "end": { "line": 132, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 26 }, "end": { "line": 126, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua": [ + { + "severity": 2, + "range": { "start": { "line": 400, "character": 43 }, "end": { "line": 400, "character": 57 } }, + "message": "Undefined type or alias `vim.Diagnostic`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 73 }, "end": { "line": 160, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 37 }, "end": { "line": 224, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 310, "character": 16 }, "end": { "line": 310, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 326, "character": 19 }, "end": { "line": 326, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 367, "character": 19 }, "end": { "line": 367, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 384, "character": 19 }, "end": { "line": 384, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 438, "character": 5 }, "end": { "line": 438, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 448, "character": 19 }, "end": { "line": 448, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 16 }, "end": { "line": 563, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 698, "character": 16 }, "end": { "line": 698, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 437, "character": 14 }, "end": { "line": 437, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 6 }, "end": { "line": 21, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 64 }, "end": { "line": 283, "character": 67 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 475, "character": 27 }, "end": { "line": 475, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 484, "character": 30 }, "end": { "line": 484, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 497, "character": 27 }, "end": { "line": 497, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 26 }, "end": { "line": 563, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 700, "character": 12 }, "end": { "line": 700, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 798, "character": 64 }, "end": { "line": 798, "character": 67 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 300, "character": 10 }, "end": { "line": 300, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 571, "character": 19 }, "end": { "line": 571, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 304, "character": 34 }, "end": { "line": 304, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 413, "character": 17 }, "end": { "line": 413, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 655, "character": 23 }, "end": { "line": 655, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 781, "character": 42 }, "end": { "line": 781, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 68 }, "end": { "line": 299, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 769, "character": 38 }, "end": { "line": 769, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 49, "character": 71 }, "end": { "line": 49, "character": 77 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/highlight.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 19 }, "end": { "line": 3, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 4 }, "end": { "line": 6, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 4 }, "end": { "line": 7, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 4 }, "end": { "line": 8, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 4 }, "end": { "line": 9, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 4 }, "end": { "line": 10, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 4 }, "end": { "line": 12, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 4 }, "end": { "line": 15, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 4 }, "end": { "line": 16, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 4 }, "end": { "line": 17, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 4 }, "end": { "line": 20, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 4 }, "end": { "line": 21, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 4 }, "end": { "line": 22, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 4 }, "end": { "line": 23, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 4 }, "end": { "line": 25, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 4 }, "end": { "line": 26, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 4 }, "end": { "line": 27, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 4 }, "end": { "line": 29, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 4 }, "end": { "line": 30, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 4 }, "end": { "line": 32, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 4 }, "end": { "line": 36, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 4 }, "end": { "line": 37, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 4 }, "end": { "line": 38, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 4 }, "end": { "line": 43, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 4 }, "end": { "line": 44, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 4 }, "end": { "line": 46, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 4 }, "end": { "line": 47, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 4 }, "end": { "line": 48, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 4 }, "end": { "line": 49, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 4 }, "end": { "line": 50, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 4 }, "end": { "line": 53, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 4 }, "end": { "line": 54, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 4 }, "end": { "line": 55, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 4 }, "end": { "line": 58, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 4 }, "end": { "line": 59, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 4 }, "end": { "line": 60, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 4 }, "end": { "line": 61, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/icons.lua": [ + { + "severity": 2, + "range": { "start": { "line": 89, "character": 6 }, "end": { "line": 89, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 8 }, "end": { "line": 90, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 10 }, "end": { "line": 92, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/loading_animation.lua": [ + { + "severity": 2, + "range": { "start": { "line": 28, "character": 11 }, "end": { "line": 28, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 10 }, "end": { "line": 13, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 4 }, "end": { "line": 100, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 28 }, "end": { "line": 45, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 9 }, "end": { "line": 34, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 62 }, "end": { "line": 34, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/mention.lua": [ + { + "severity": 2, + "range": { "start": { "line": 103, "character": 2 }, "end": { "line": 103, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 27 }, "end": { "line": 4, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 2 }, "end": { "line": 104, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 2 }, "end": { "line": 105, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 8 }, "end": { "line": 11, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 6 }, "end": { "line": 33, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 23 }, "end": { "line": 94, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 4 }, "end": { "line": 112, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 32 }, "end": { "line": 14, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 26 }, "end": { "line": 56, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 7 }, "end": { "line": 132, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 7 }, "end": { "line": 137, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 116, "character": 25 }, "end": { "line": 116, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 27 }, "end": { "line": 137, "character": 31 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/navigation.lua": [ + { + "severity": 2, + "range": { "start": { "line": 53, "character": 2 }, "end": { "line": 53, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 2 }, "end": { "line": 86, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 23 }, "end": { "line": 32, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 19 }, "end": { "line": 34, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 21 }, "end": { "line": 52, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 23 }, "end": { "line": 66, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 19 }, "end": { "line": 68, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 6 }, "end": { "line": 47, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 6 }, "end": { "line": 82, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output.lua": [ + { + "severity": 2, + "range": { "start": { "line": 94, "character": 9 }, "end": { "line": 94, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 9 }, "end": { "line": 110, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 30 }, "end": { "line": 141, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 30 }, "end": { "line": 145, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output_window.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 14 }, "end": { "line": 4, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 2 }, "end": { "line": 39, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 2 }, "end": { "line": 40, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 2 }, "end": { "line": 42, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 2 }, "end": { "line": 43, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 2 }, "end": { "line": 44, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 2 }, "end": { "line": 45, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 2 }, "end": { "line": 47, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 2 }, "end": { "line": 48, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 2 }, "end": { "line": 49, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 2 }, "end": { "line": 50, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 2 }, "end": { "line": 51, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 2 }, "end": { "line": 61, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 2 }, "end": { "line": 89, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 2 }, "end": { "line": 90, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 2 }, "end": { "line": 91, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 2 }, "end": { "line": 106, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 2 }, "end": { "line": 141, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 158, "character": 2 }, "end": { "line": 158, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 2 }, "end": { "line": 168, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 9 }, "end": { "line": 69, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 4 }, "end": { "line": 139, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 8 }, "end": { "line": 148, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 8 }, "end": { "line": 149, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 21 }, "end": { "line": 7, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 22 }, "end": { "line": 58, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 12 }, "end": { "line": 132, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 11 }, "end": { "line": 30, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 6 }, "end": { "line": 162, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 172, "character": 6 }, "end": { "line": 172, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 656, "character": 2 }, "end": { "line": 656, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 733, "character": 2 }, "end": { "line": 733, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 4 }, "end": { "line": 507, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 685, "character": 4 }, "end": { "line": 685, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 699, "character": 4 }, "end": { "line": 699, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 194, "character": 18 }, "end": { "line": 194, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 17 }, "end": { "line": 195, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 21 }, "end": { "line": 197, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 4 }, "end": { "line": 213, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 669, "character": 9 }, "end": { "line": 669, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 31 }, "end": { "line": 189, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 453, "character": 30 }, "end": { "line": 453, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 54 }, "end": { "line": 507, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 73 }, "end": { "line": 507, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 685, "character": 36 }, "end": { "line": 685, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 316, "character": 23 }, "end": { "line": 316, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 9 }, "end": { "line": 30, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 9 }, "end": { "line": 32, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/session_picker.lua": [ + { + "severity": 2, + "range": { "start": { "line": 423, "character": 2 }, "end": { "line": 423, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 16 }, "end": { "line": 233, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 25 }, "end": { "line": 53, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 365, "character": 6 }, "end": { "line": 365, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 396, "character": 6 }, "end": { "line": 396, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 401, "character": 6 }, "end": { "line": 401, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 8 }, "end": { "line": 342, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 10 }, "end": { "line": 324, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 8 }, "end": { "line": 402, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 22 }, "end": { "line": 249, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 51 }, "end": { "line": 402, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 69 }, "end": { "line": 402, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 22 }, "end": { "line": 54, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 253, "character": 20 }, "end": { "line": 253, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 20 }, "end": { "line": 276, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 343, "character": 22 }, "end": { "line": 343, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 24 }, "end": { "line": 107, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/timer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 31, "character": 18 }, "end": { "line": 31, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 16 }, "end": { "line": 25, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 53, "character": 15 }, "end": { "line": 53, "character": 21 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 32, "character": 45 }, "end": { "line": 32, "character": 51 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/helpers.lua": [ + { + "severity": 2, + "range": { "start": { "line": 112, "character": 2 }, "end": { "line": 112, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 2 }, "end": { "line": 64, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 2 }, "end": { "line": 78, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 2 }, "end": { "line": 98, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 109, "character": 26 }, "end": { "line": 109, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 25 }, "end": { "line": 96, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 9 }, "end": { "line": 65, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 8 }, "end": { "line": 91, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 4 }, "end": { "line": 102, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 157, "character": 9 }, "end": { "line": 157, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 10 }, "end": { "line": 237, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 21 }, "end": { "line": 249, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 19 }, "end": { "line": 45, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 6 }, "end": { "line": 122, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 264, "character": 14 }, "end": { "line": 264, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 20 }, "end": { "line": 39, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 15 }, "end": { "line": 70, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 71, "character": 10 }, "end": { "line": 71, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 7 }, "end": { "line": 86, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 25 }, "end": { "line": 84, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 87, "character": 12 }, "end": { "line": 87, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 12 }, "end": { "line": 262, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 263, "character": 15 }, "end": { "line": 263, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 177, "character": 28 }, "end": { "line": 177, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 17 }, "end": { "line": 180, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 39 }, "end": { "line": 197, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 36 }, "end": { "line": 199, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/manual/init_replay.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 0 }, "end": { "line": 21, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 0 }, "end": { "line": 22, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 0 }, "end": { "line": 23, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 0 }, "end": { "line": 27, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 0 }, "end": { "line": 29, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 0 }, "end": { "line": 19, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 0 }, "end": { "line": 24, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 0, "character": 20 }, "end": { "line": 0, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 2 }, "end": { "line": 16, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 3 }, "end": { "line": 8, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 5 }, "end": { "line": 10, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/manual/renderer_replay.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 2 }, "end": { "line": 35, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 2 }, "end": { "line": 134, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 2 }, "end": { "line": 200, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 2 }, "end": { "line": 205, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 2 }, "end": { "line": 265, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 2 }, "end": { "line": 266, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 284, "character": 2 }, "end": { "line": 284, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 289, "character": 2 }, "end": { "line": 289, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 293, "character": 2 }, "end": { "line": 293, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 2 }, "end": { "line": 298, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 303, "character": 2 }, "end": { "line": 303, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 2 }, "end": { "line": 307, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 311, "character": 2 }, "end": { "line": 311, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 315, "character": 2 }, "end": { "line": 315, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 2 }, "end": { "line": 319, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 331, "character": 2 }, "end": { "line": 331, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 336, "character": 2 }, "end": { "line": 336, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 337, "character": 2 }, "end": { "line": 337, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 338, "character": 2 }, "end": { "line": 338, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 339, "character": 2 }, "end": { "line": 339, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 340, "character": 2 }, "end": { "line": 340, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 341, "character": 2 }, "end": { "line": 341, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 4 }, "end": { "line": 112, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 152, "character": 4 }, "end": { "line": 152, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 4 }, "end": { "line": 181, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 4 }, "end": { "line": 189, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 4 }, "end": { "line": 262, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 4 }, "end": { "line": 356, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 20 }, "end": { "line": 16, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 69 }, "end": { "line": 35, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 6 }, "end": { "line": 70, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 21 }, "end": { "line": 134, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 16 }, "end": { "line": 162, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 163, "character": 19 }, "end": { "line": 163, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 14 }, "end": { "line": 168, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 57 }, "end": { "line": 200, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 4 }, "end": { "line": 207, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 246, "character": 4 }, "end": { "line": 246, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 14 }, "end": { "line": 255, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 256, "character": 15 }, "end": { "line": 256, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 257, "character": 21 }, "end": { "line": 257, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 4 }, "end": { "line": 333, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 27 }, "end": { "line": 26, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 176, "character": 6 }, "end": { "line": 176, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 47 }, "end": { "line": 19, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 61 }, "end": { "line": 28, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 152, "character": 45 }, "end": { "line": 152, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 173, "character": 17 }, "end": { "line": 173, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 49 }, "end": { "line": 181, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 59 }, "end": { "line": 189, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 6 }, "end": { "line": 213, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 54 }, "end": { "line": 258, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 325, "character": 6 }, "end": { "line": 325, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 79 }, "end": { "line": 356, "character": 82 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 6 }, "end": { "line": 48, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 45 }, "end": { "line": 70, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 18 }, "end": { "line": 222, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 21 }, "end": { "line": 223, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 81 }, "end": { "line": 333, "character": 84 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 12 }, "end": { "line": 54, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 12 }, "end": { "line": 55, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 176, "character": 66 }, "end": { "line": 176, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 208, "character": 13 }, "end": { "line": 208, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 8 }, "end": { "line": 51, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 8 }, "end": { "line": 52, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 325, "character": 61 }, "end": { "line": 325, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/init.lua": [ + { + "severity": 2, + "range": { "start": { "line": 32, "character": 0 }, "end": { "line": 32, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 0 }, "end": { "line": 9, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 0 }, "end": { "line": 26, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 27 }, "end": { "line": 4, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 24 }, "end": { "line": 5, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 20 }, "end": { "line": 8, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 3 }, "end": { "line": 15, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 5 }, "end": { "line": 17, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 2 }, "end": { "line": 12, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 2 }, "end": { "line": 62, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 2 }, "end": { "line": 74, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 2 }, "end": { "line": 80, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 4 }, "end": { "line": 63, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 4 }, "end": { "line": 64, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 24 }, "end": { "line": 13, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 22 }, "end": { "line": 17, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 4 }, "end": { "line": 65, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 26 }, "end": { "line": 27, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 2 }, "end": { "line": 4, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 2 }, "end": { "line": 15, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 2 }, "end": { "line": 60, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 7, "character": 0 }, "end": { "line": 7, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 2 }, "end": { "line": 26, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 2 }, "end": { "line": 114, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 2 }, "end": { "line": 144, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 4 }, "end": { "line": 66, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 4 }, "end": { "line": 86, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 115, "character": 4 }, "end": { "line": 115, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 4 }, "end": { "line": 155, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 165, "character": 4 }, "end": { "line": 165, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 4 }, "end": { "line": 175, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 4 }, "end": { "line": 187, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 198, "character": 4 }, "end": { "line": 198, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 4 }, "end": { "line": 205, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 9 }, "end": { "line": 12, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 6 }, "end": { "line": 90, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 22 }, "end": { "line": 94, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 31 }, "end": { "line": 199, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 66 }, "end": { "line": 201, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 2 }, "end": { "line": 18, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 2 }, "end": { "line": 51, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 2 }, "end": { "line": 66, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 2 }, "end": { "line": 81, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 4 }, "end": { "line": 10, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 24 }, "end": { "line": 9, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 16 }, "end": { "line": 61, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 16 }, "end": { "line": 62, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 16 }, "end": { "line": 63, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 16 }, "end": { "line": 76, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 17 }, "end": { "line": 77, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 16 }, "end": { "line": 78, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 23 }, "end": { "line": 35, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 23 }, "end": { "line": 59, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 67, "character": 23 }, "end": { "line": 74, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 23 }, "end": { "line": 90, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 2 }, "end": { "line": 26, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 22 }, "end": { "line": 11, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 20 }, "end": { "line": 13, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 0 }, "end": { "line": 6, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 2 }, "end": { "line": 32, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 109, "character": 2 }, "end": { "line": 109, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 2 }, "end": { "line": 232, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 4 }, "end": { "line": 21, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 4 }, "end": { "line": 65, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 4 }, "end": { "line": 81, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 4 }, "end": { "line": 90, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 4 }, "end": { "line": 106, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 4 }, "end": { "line": 110, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 4 }, "end": { "line": 121, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 4 }, "end": { "line": 137, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 157, "character": 4 }, "end": { "line": 157, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 177, "character": 4 }, "end": { "line": 177, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 198, "character": 4 }, "end": { "line": 198, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 211, "character": 4 }, "end": { "line": 211, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 4 }, "end": { "line": 223, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 4 }, "end": { "line": 233, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 24 }, "end": { "line": 37, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 32 }, "end": { "line": 41, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 18 }, "end": { "line": 42, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 4 }, "end": { "line": 69, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 4 }, "end": { "line": 73, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 4 }, "end": { "line": 77, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 4 }, "end": { "line": 102, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 4 }, "end": { "line": 103, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 4 }, "end": { "line": 104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 32 }, "end": { "line": 38, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 36 }, "end": { "line": 39, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 36 }, "end": { "line": 40, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 6 }, "end": { "line": 212, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/util.lua": [ + { + "severity": 2, + "range": { "start": { "line": 61, "character": 9 }, "end": { "line": 61, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 18 }, "end": { "line": 15, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 16 }, "end": { "line": 27, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 16 }, "end": { "line": 281, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 414, "character": 9 }, "end": { "line": 414, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 419, "character": 9 }, "end": { "line": 419, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 450, "character": 24 }, "end": { "line": 450, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 19 }, "end": { "line": 14, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 19 }, "end": { "line": 16, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 253, "character": 25 }, "end": { "line": 253, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 404, "character": 20 }, "end": { "line": 404, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 12 }, "end": { "line": 237, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 28 }, "end": { "line": 322, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 16 }, "end": { "line": 247, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 21 }, "end": { "line": 319, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 343, "character": 20 }, "end": { "line": 343, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 203, "character": 27 }, "end": { "line": 203, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 37 }, "end": { "line": 255, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 31 }, "end": { "line": 258, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 41 }, "end": { "line": 319, "character": 49 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 282, "character": 7 }, "end": { "line": 282, "character": 13 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 238, "character": 11 }, "end": { "line": 238, "character": 17 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/state.lua": [ + { + "severity": 2, + "range": { "start": { "line": 133, "character": 2 }, "end": { "line": 133, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 161, "character": 14 }, "end": { "line": 161, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 174, "character": 14 }, "end": { "line": 174, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 11 }, "end": { "line": 195, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 10 }, "end": { "line": 138, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 10 }, "end": { "line": 187, "character": 12 } }, + "message": "Missing required fields in type `OpencodeState`: `input_content`, `is_opencode_focused`, `current_mode`, `last_output`, `restore_points`, `cost`, `tokens_count`, `job_count`, `api_client`, `required_version`, `append`, `remove`, `subscribe`, `subscribe`, `unsubscribe`, `is_running`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 2 }, "end": { "line": 13, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 2 }, "end": { "line": 114, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 4 }, "end": { "line": 9, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 4 }, "end": { "line": 68, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 117, "character": 4 }, "end": { "line": 117, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 22 }, "end": { "line": 6, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 4 }, "end": { "line": 108, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 4 }, "end": { "line": 156, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 6 }, "end": { "line": 26, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/session.lua": [ + { + "severity": 2, + "range": { "start": { "line": 54, "character": 9 }, "end": { "line": 54, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 4 }, "end": { "line": 50, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 24 }, "end": { "line": 114, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 15 }, "end": { "line": 20, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 15 }, "end": { "line": 28, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 15 }, "end": { "line": 36, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 15 }, "end": { "line": 94, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 135, "character": 24 }, "end": { "line": 135, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 33 }, "end": { "line": 145, "character": 36 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 69 }, "end": { "line": 11, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 51 }, "end": { "line": 50, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 72 }, "end": { "line": 50, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 72, "character": 17 }, "end": { "line": 72, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 31 }, "end": { "line": 144, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 178, "character": 50 }, "end": { "line": 178, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 31 }, "end": { "line": 95, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 46 }, "end": { "line": 95, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/context.lua": [ + { + "severity": 2, + "range": { "start": { "line": 206, "character": 2 }, "end": { "line": 206, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 2 }, "end": { "line": 224, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 246, "character": 4 }, "end": { "line": 246, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 4 }, "end": { "line": 247, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 4 }, "end": { "line": 248, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 288, "character": 4 }, "end": { "line": 288, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 24 }, "end": { "line": 74, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 18 }, "end": { "line": 206, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 209, "character": 15 }, "end": { "line": 209, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 4 }, "end": { "line": 218, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 18 }, "end": { "line": 224, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 12 }, "end": { "line": 50, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 12 }, "end": { "line": 53, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 12 }, "end": { "line": 56, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 24 }, "end": { "line": 265, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 270, "character": 7 }, "end": { "line": 270, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 208, "character": 24 }, "end": { "line": 208, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 23 }, "end": { "line": 106, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 251, "character": 14 }, "end": { "line": 251, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 26 }, "end": { "line": 267, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 59 }, "end": { "line": 96, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 37 }, "end": { "line": 218, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 18 }, "end": { "line": 206, "character": 76 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 78 }, "end": { "line": 206, "character": 81 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 18 }, "end": { "line": 224, "character": 72 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 74 }, "end": { "line": 224, "character": 77 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 58 }, "end": { "line": 206, "character": 62 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 64 }, "end": { "line": 206, "character": 69 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 71 }, "end": { "line": 206, "character": 75 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 54 }, "end": { "line": 224, "character": 58 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 60 }, "end": { "line": 224, "character": 65 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 67 }, "end": { "line": 224, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 208, "character": 17 }, "end": { "line": 208, "character": 23 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/input_window.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 2 }, "end": { "line": 6, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 2 }, "end": { "line": 54, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 2 }, "end": { "line": 55, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 2 }, "end": { "line": 95, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 97, "character": 2 }, "end": { "line": 97, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 2 }, "end": { "line": 98, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 2 }, "end": { "line": 100, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 2 }, "end": { "line": 101, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 2 }, "end": { "line": 104, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 2 }, "end": { "line": 105, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 2 }, "end": { "line": 106, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 2 }, "end": { "line": 126, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 2 }, "end": { "line": 170, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 2 }, "end": { "line": 179, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 2 }, "end": { "line": 197, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 2 }, "end": { "line": 206, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 225, "character": 2 }, "end": { "line": 225, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 2 }, "end": { "line": 245, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 2 }, "end": { "line": 255, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 8 }, "end": { "line": 44, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 8 }, "end": { "line": 45, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 16 }, "end": { "line": 81, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 22 }, "end": { "line": 83, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 4 }, "end": { "line": 90, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 50 }, "end": { "line": 195, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 20 }, "end": { "line": 5, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 22 }, "end": { "line": 22, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 22 }, "end": { "line": 121, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 23 }, "end": { "line": 122, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 4 }, "end": { "line": 146, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 4 }, "end": { "line": 162, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 54 }, "end": { "line": 170, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 16 }, "end": { "line": 181, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 215, "character": 16 }, "end": { "line": 215, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 20 }, "end": { "line": 218, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 235, "character": 16 }, "end": { "line": 235, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 19 }, "end": { "line": 88, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 11 }, "end": { "line": 31, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 37 }, "end": { "line": 53, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 43 }, "end": { "line": 90, "character": 46 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 135, "character": 18 }, "end": { "line": 135, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 18 }, "end": { "line": 138, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 22 }, "end": { "line": 139, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 56 }, "end": { "line": 162, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 26 }, "end": { "line": 258, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/ui.lua": [ + { + "severity": 2, + "range": { "start": { "line": 234, "character": 2 }, "end": { "line": 234, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 2 }, "end": { "line": 68, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 2 }, "end": { "line": 73, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 2 }, "end": { "line": 74, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 117, "character": 2 }, "end": { "line": 117, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 2 }, "end": { "line": 136, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 8 }, "end": { "line": 22, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 8 }, "end": { "line": 23, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 8 }, "end": { "line": 25, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 8 }, "end": { "line": 26, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 8 }, "end": { "line": 27, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 8 }, "end": { "line": 28, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 4 }, "end": { "line": 54, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 9 }, "end": { "line": 58, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 2 }, "end": { "line": 80, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 22 }, "end": { "line": 147, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 16 }, "end": { "line": 164, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 4 }, "end": { "line": 191, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 22 }, "end": { "line": 218, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 18 }, "end": { "line": 38, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 120, "character": 10 }, "end": { "line": 120, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 10 }, "end": { "line": 139, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 42 }, "end": { "line": 85, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 29 }, "end": { "line": 86, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 5 }, "end": { "line": 122, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 124, "character": 6 }, "end": { "line": 124, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 123, "character": 7 }, "end": { "line": 123, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/git_review.lua": [ + { + "severity": 2, + "range": { "start": { "line": 10, "character": 16 }, "end": { "line": 10, "character": 30 } }, + "message": "Undefined type or alias `vim.SystemOpts`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 2 }, "end": { "line": 19, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 2 }, "end": { "line": 107, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 2 }, "end": { "line": 356, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 4 }, "end": { "line": 15, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 11 }, "end": { "line": 22, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 20 }, "end": { "line": 106, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 2 }, "end": { "line": 292, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 17 }, "end": { "line": 20, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 2 }, "end": { "line": 260, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 4 }, "end": { "line": 128, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 4 }, "end": { "line": 141, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 6 }, "end": { "line": 224, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 6 }, "end": { "line": 233, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 234, "character": 6 }, "end": { "line": 234, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 4 }, "end": { "line": 249, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 261, "character": 4 }, "end": { "line": 261, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 4 }, "end": { "line": 283, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 4 }, "end": { "line": 299, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 318, "character": 4 }, "end": { "line": 318, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 373, "character": 6 }, "end": { "line": 373, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 375, "character": 6 }, "end": { "line": 375, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 8 }, "end": { "line": 68, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 8 }, "end": { "line": 74, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 8 }, "end": { "line": 80, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 4 }, "end": { "line": 149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 25 }, "end": { "line": 212, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 21 }, "end": { "line": 213, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 310, "character": 4 }, "end": { "line": 310, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 49 }, "end": { "line": 78, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 6 }, "end": { "line": 150, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 172, "character": 6 }, "end": { "line": 172, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 190, "character": 6 }, "end": { "line": 190, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 6 }, "end": { "line": 255, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 329, "character": 4 }, "end": { "line": 329, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 271, "character": 8 }, "end": { "line": 271, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 305, "character": 6 }, "end": { "line": 305, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 6 }, "end": { "line": 324, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 330, "character": 6 }, "end": { "line": 330, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 373, "character": 72 }, "end": { "line": 373, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 375, "character": 76 }, "end": { "line": 375, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 29 }, "end": { "line": 40, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 228, "character": 7 }, "end": { "line": 228, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 5 }, "end": { "line": 287, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 13 }, "end": { "line": 262, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 340, "character": 10 }, "end": { "line": 340, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 15 }, "end": { "line": 151, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 331, "character": 15 }, "end": { "line": 331, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/server_job.lua": [ + { + "severity": 2, + "range": { "start": { "line": 100, "character": 12 }, "end": { "line": 100, "character": 15 } }, + "message": "Undefined type or alias `Job`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 35 }, "end": { "line": 11, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 6 }, "end": { "line": 114, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 8 }, "end": { "line": 56, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 25 }, "end": { "line": 64, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 8 }, "end": { "line": 118, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 124, "character": 25 }, "end": { "line": 124, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 51 }, "end": { "line": 114, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 69 }, "end": { "line": 114, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 10 }, "end": { "line": 57, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 76 }, "end": { "line": 118, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 12 }, "end": { "line": 39, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 12 }, "end": { "line": 46, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 62 }, "end": { "line": 57, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 14 }, "end": { "line": 40, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 14 }, "end": { "line": 47, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 72 }, "end": { "line": 40, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 66 }, "end": { "line": 47, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 21, "character": 0 }, "end": { "line": 21, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 2 }, "end": { "line": 92, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 2 }, "end": { "line": 100, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 111, "character": 2 }, "end": { "line": 111, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 2 }, "end": { "line": 242, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 421, "character": 2 }, "end": { "line": 421, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 486, "character": 2 }, "end": { "line": 486, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 4 }, "end": { "line": 114, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 4 }, "end": { "line": 119, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 4 }, "end": { "line": 129, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 4 }, "end": { "line": 162, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 4 }, "end": { "line": 180, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 4 }, "end": { "line": 199, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 4 }, "end": { "line": 212, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 4 }, "end": { "line": 245, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 274, "character": 4 }, "end": { "line": 274, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 280, "character": 4 }, "end": { "line": 280, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 4 }, "end": { "line": 287, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 306, "character": 4 }, "end": { "line": 306, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 338, "character": 4 }, "end": { "line": 338, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 370, "character": 4 }, "end": { "line": 370, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 396, "character": 4 }, "end": { "line": 396, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 422, "character": 4 }, "end": { "line": 422, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 446, "character": 4 }, "end": { "line": 446, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 470, "character": 4 }, "end": { "line": 470, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 487, "character": 4 }, "end": { "line": 487, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 509, "character": 4 }, "end": { "line": 509, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 6 }, "end": { "line": 249, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 6 }, "end": { "line": 258, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 269, "character": 6 }, "end": { "line": 269, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 6 }, "end": { "line": 250, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 251, "character": 6 }, "end": { "line": 251, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 259, "character": 6 }, "end": { "line": 259, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 6 }, "end": { "line": 262, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 6 }, "end": { "line": 265, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 15 }, "end": { "line": 249, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 16 }, "end": { "line": 258, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 173, "character": 30 }, "end": { "line": 173, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 188, "character": 29 }, "end": { "line": 188, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 526, "character": 11 }, "end": { "line": 526, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 0 }, "end": { "line": 29, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 0 }, "end": { "line": 40, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 0 }, "end": { "line": 50, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 0 }, "end": { "line": 94, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 111, "character": 0 }, "end": { "line": 111, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 2 }, "end": { "line": 45, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 2 }, "end": { "line": 52, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 2 }, "end": { "line": 69, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 2 }, "end": { "line": 75, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 2 }, "end": { "line": 95, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 2 }, "end": { "line": 112, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 2 }, "end": { "line": 118, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 125, "character": 2 }, "end": { "line": 125, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 2 }, "end": { "line": 132, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 2 }, "end": { "line": 136, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 4 }, "end": { "line": 119, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 4 }, "end": { "line": 126, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 97, "character": 35 }, "end": { "line": 97, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 42 }, "end": { "line": 104, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 54 }, "end": { "line": 105, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 17 }, "end": { "line": 12, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua": [ + { + "severity": 3, + "range": { "start": { "line": 246, "character": 8 }, "end": { "line": 246, "character": 21 } }, + "message": "Global variable in lowercase initial, Did you miss `local` or misspell it?", + "source": "Lua Diagnostics.", + "code": "lowercase-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 0 }, "end": { "line": 30, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 2 }, "end": { "line": 36, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 2 }, "end": { "line": 130, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 174, "character": 2 }, "end": { "line": 174, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 2 }, "end": { "line": 199, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 263, "character": 2 }, "end": { "line": 263, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 4 }, "end": { "line": 103, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 131, "character": 4 }, "end": { "line": 131, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 4 }, "end": { "line": 144, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 4 }, "end": { "line": 151, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 4 }, "end": { "line": 175, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 4 }, "end": { "line": 200, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 221, "character": 4 }, "end": { "line": 221, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 240, "character": 4 }, "end": { "line": 240, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 278, "character": 4 }, "end": { "line": 278, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 284, "character": 4 }, "end": { "line": 284, "character": 14 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 290, "character": 4 }, "end": { "line": 290, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 297, "character": 4 }, "end": { "line": 297, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 4 }, "end": { "line": 307, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 317, "character": 4 }, "end": { "line": 317, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 22 }, "end": { "line": 38, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 24 }, "end": { "line": 40, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 4 }, "end": { "line": 104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 26 }, "end": { "line": 39, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 21 }, "end": { "line": 37, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 285, "character": 6 }, "end": { "line": 285, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 301, "character": 6 }, "end": { "line": 301, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 311, "character": 6 }, "end": { "line": 311, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 321, "character": 6 }, "end": { "line": 321, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 214, "character": 6 }, "end": { "line": 214, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 279, "character": 24 }, "end": { "line": 279, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 286, "character": 6 }, "end": { "line": 286, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 6 }, "end": { "line": 291, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 6 }, "end": { "line": 298, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 308, "character": 6 }, "end": { "line": 308, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 318, "character": 6 }, "end": { "line": 318, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 280, "character": 28 }, "end": { "line": 280, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 21 }, "end": { "line": 27, "character": 3 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 28 }, "end": { "line": 94, "character": 5 } }, + "message": "Missing required fields in type `OpencodeServer`: `spawn_promise`, `shutdown_promise`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 29 }, "end": { "line": 146, "character": 51 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 29 }, "end": { "line": 202, "character": 45 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 243, "character": 29 }, "end": { "line": 243, "character": 45 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 254, "character": 20 }, "end": { "line": 254, "character": 58 } }, + "message": "Missing required fields in type `OpencodeContextConfig`: `enabled`, `cursor_data`, `diagnostics`, `selection`, `agents`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/curl.lua": [ + { + "severity": 2, + "range": { "start": { "line": 39, "character": 16 }, "end": { "line": 39, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 4 }, "end": { "line": 134, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 16 }, "end": { "line": 81, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 15 }, "end": { "line": 126, "character": 16 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 21 }, "end": { "line": 126, "character": 30 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api_client.lua": [ + { + "severity": 2, + "range": { "start": { "line": 382, "character": 12 }, "end": { "line": 382, "character": 15 } }, + "message": "Undefined type or alias `Job`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 164 }, "end": { "line": 223, "character": 168 } }, + "message": "Undefined type or alias `Part`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 28 }, "end": { "line": 393, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 45 }, "end": { "line": 393, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua": [ + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 148, "character": 118 }, "end": { "line": 148, "character": 118 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 149, "character": 121 }, "end": { "line": 149, "character": 121 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 150, "character": 108 }, "end": { "line": 150, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 151, "character": 108 }, "end": { "line": 151, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 152, "character": 117 }, "end": { "line": 152, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 153, "character": 117 }, "end": { "line": 153, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 154, "character": 112 }, "end": { "line": 154, "character": 112 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 155, "character": 102 }, "end": { "line": 155, "character": 102 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 156, "character": 108 }, "end": { "line": 156, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 157, "character": 108 }, "end": { "line": 157, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 158, "character": 104 }, "end": { "line": 158, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 159, "character": 114 }, "end": { "line": 159, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 160, "character": 114 }, "end": { "line": 160, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 161, "character": 100 }, "end": { "line": 161, "character": 100 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 162, "character": 117 }, "end": { "line": 162, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 163, "character": 110 }, "end": { "line": 163, "character": 110 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 164, "character": 104 }, "end": { "line": 164, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 165, "character": 115 }, "end": { "line": 165, "character": 115 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 166, "character": 109 }, "end": { "line": 166, "character": 109 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 167, "character": 113 }, "end": { "line": 167, "character": 113 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 168, "character": 126 }, "end": { "line": 168, "character": 126 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 181, "character": 118 }, "end": { "line": 181, "character": 118 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 182, "character": 121 }, "end": { "line": 182, "character": 121 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 183, "character": 108 }, "end": { "line": 183, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 184, "character": 108 }, "end": { "line": 184, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 185, "character": 117 }, "end": { "line": 185, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 186, "character": 117 }, "end": { "line": 186, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 187, "character": 112 }, "end": { "line": 187, "character": 112 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 188, "character": 102 }, "end": { "line": 188, "character": 102 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 189, "character": 108 }, "end": { "line": 189, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 190, "character": 108 }, "end": { "line": 190, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 191, "character": 104 }, "end": { "line": 191, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 192, "character": 114 }, "end": { "line": 192, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 193, "character": 114 }, "end": { "line": 193, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 194, "character": 100 }, "end": { "line": 194, "character": 100 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 195, "character": 117 }, "end": { "line": 195, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 196, "character": 110 }, "end": { "line": 196, "character": 110 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 197, "character": 104 }, "end": { "line": 197, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 198, "character": 115 }, "end": { "line": 198, "character": 115 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 199, "character": 109 }, "end": { "line": 199, "character": 109 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 200, "character": 113 }, "end": { "line": 200, "character": 113 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 201, "character": 126 }, "end": { "line": 201, "character": 126 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 39 }, "end": { "line": 281, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 308, "character": 10 }, "end": { "line": 308, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 361, "character": 8 }, "end": { "line": 361, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 2 }, "end": { "line": 9, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 2 }, "end": { "line": 15, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 2 }, "end": { "line": 64, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 2 }, "end": { "line": 77, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 2 }, "end": { "line": 93, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 113, "character": 2 }, "end": { "line": 113, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 3, "character": 2 }, "end": { "line": 3, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 2 }, "end": { "line": 14, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 2 }, "end": { "line": 25, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 2 }, "end": { "line": 36, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 2 }, "end": { "line": 49, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 2 }, "end": { "line": 60, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 19 }, "end": { "line": 9, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 19 }, "end": { "line": 10, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 19 }, "end": { "line": 20, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 19 }, "end": { "line": 21, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 2 }, "end": { "line": 6, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 2 }, "end": { "line": 12, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua": [ + { + "severity": 2, + "range": { "start": { "line": 339, "character": 2 }, "end": { "line": 339, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 28 }, "end": { "line": 16, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 17 }, "end": { "line": 107, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 4 }, "end": { "line": 130, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 28 }, "end": { "line": 169, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 4 }, "end": { "line": 250, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 4 }, "end": { "line": 267, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 272, "character": 4 }, "end": { "line": 272, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 4 }, "end": { "line": 291, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 4 }, "end": { "line": 299, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 6 }, "end": { "line": 145, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 9 }, "end": { "line": 298, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 6 }, "end": { "line": 61, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 5 }, "end": { "line": 249, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 6 }, "end": { "line": 319, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 59 }, "end": { "line": 101, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 55 }, "end": { "line": 130, "character": 58 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 6 }, "end": { "line": 202, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 6 }, "end": { "line": 252, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 97 }, "end": { "line": 267, "character": 100 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 274, "character": 6 }, "end": { "line": 274, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 39 }, "end": { "line": 291, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 301, "character": 6 }, "end": { "line": 301, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 326, "character": 24 }, "end": { "line": 326, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 57 }, "end": { "line": 145, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 75 }, "end": { "line": 145, "character": 78 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 8 }, "end": { "line": 222, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 8 }, "end": { "line": 226, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 19 }, "end": { "line": 258, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 70 }, "end": { "line": 61, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 6 }, "end": { "line": 156, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 46 }, "end": { "line": 319, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 62 }, "end": { "line": 202, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 38 }, "end": { "line": 222, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 51 }, "end": { "line": 156, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 69 }, "end": { "line": 156, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 59 }, "end": { "line": 136, "character": 64 } }, + "message": "Undefined field `parts`.", + "source": "Lua Diagnostics.", + "code": "undefined-field" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 38 }, "end": { "line": 136, "character": 42 } }, + "message": "Undefined field `info`.", + "source": "Lua Diagnostics.", + "code": "undefined-field" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/health.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 15 }, "end": { "line": 2, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 9 }, "end": { "line": 6, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 9 }, "end": { "line": 114, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 14 }, "end": { "line": 154, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 17 }, "end": { "line": 14, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 5 }, "end": { "line": 148, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 18 }, "end": { "line": 147, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/history.lua": [ + { + "severity": 2, + "range": { "start": { "line": 9, "character": 29 }, "end": { "line": 9, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/id.lua": [ + { + "severity": 2, + "range": { "start": { "line": 139, "character": 9 }, "end": { "line": 139, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 9 }, "end": { "line": 101, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 11 }, "end": { "line": 119, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 39 }, "end": { "line": 66, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/keymap.lua": [ + { + "severity": 2, + "range": { "start": { "line": 38, "character": 9 }, "end": { "line": 38, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 9 }, "end": { "line": 49, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 19 }, "end": { "line": 15, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 8 }, "end": { "line": 21, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 12 }, "end": { "line": 74, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 12 }, "end": { "line": 75, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 8 }, "end": { "line": 19, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 8 }, "end": { "line": 65, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 98 }, "end": { "line": 21, "character": 101 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/opencode_server.lua": [ + { + "severity": 2, + "range": { "start": { "line": 58, "character": 34 }, "end": { "line": 58, "character": 53 } }, + "message": "Undefined type or alias `vim.SystemCompleted`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 13 }, "end": { "line": 66, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 12 }, "end": { "line": 18, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/provider.lua": [ + { + "severity": 2, + "range": { "start": { "line": 22, "character": 2 }, "end": { "line": 22, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/promise.lua": [ + { + "severity": 2, + "range": { "start": { "line": 53, "character": 10 }, "end": { "line": 53, "character": 15 } }, + "message": "Undefined param `error`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-param" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 77 }, "end": { "line": 5, "character": 78 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 2, "character": 37 }, "end": { "line": 2, "character": 38 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 34 }, "end": { "line": 5, "character": 35 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 2, "character": 62 }, "end": { "line": 2, "character": 63 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 24 }, "end": { "line": 42, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 25 }, "end": { "line": 62, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 178, "character": 18 }, "end": { "line": 178, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 26 }, "end": { "line": 103, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 27 }, "end": { "line": 150, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 0 }, "end": { "line": 6, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 2 }, "end": { "line": 38, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 2 }, "end": { "line": 44, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 2 }, "end": { "line": 242, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 359, "character": 2 }, "end": { "line": 359, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 425, "character": 2 }, "end": { "line": 425, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 4 }, "end": { "line": 79, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 4 }, "end": { "line": 121, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 243, "character": 4 }, "end": { "line": 243, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 4 }, "end": { "line": 276, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 309, "character": 4 }, "end": { "line": 309, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 4 }, "end": { "line": 342, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 360, "character": 4 }, "end": { "line": 360, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 4 }, "end": { "line": 393, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 426, "character": 4 }, "end": { "line": 426, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 465, "character": 4 }, "end": { "line": 465, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 496, "character": 4 }, "end": { "line": 496, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 23 }, "end": { "line": 21, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 26 }, "end": { "line": 20, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 6 }, "end": { "line": 281, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 306, "character": 6 }, "end": { "line": 306, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 365, "character": 6 }, "end": { "line": 365, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 389, "character": 6 }, "end": { "line": 389, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 278, "character": 30 }, "end": { "line": 278, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 362, "character": 30 }, "end": { "line": 362, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 390, "character": 6 }, "end": { "line": 390, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 421, "character": 6 }, "end": { "line": 421, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 461, "character": 6 }, "end": { "line": 461, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 493, "character": 6 }, "end": { "line": 493, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 521, "character": 6 }, "end": { "line": 521, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 456, "character": 21 }, "end": { "line": 456, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 457, "character": 21 }, "end": { "line": 457, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 458, "character": 21 }, "end": { "line": 458, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 295, "character": 19 }, "end": { "line": 295, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 369, "character": 20 }, "end": { "line": 369, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 378, "character": 19 }, "end": { "line": 378, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 394, "character": 20 }, "end": { "line": 394, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 430, "character": 20 }, "end": { "line": 430, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 469, "character": 20 }, "end": { "line": 469, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 500, "character": 20 }, "end": { "line": 500, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 428, "character": 33 }, "end": { "line": 428, "character": 48 } }, + "message": "Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 467, "character": 33 }, "end": { "line": 467, "character": 48 } }, + "message": "Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/plugin/opencode.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1, "character": 3 }, "end": { "line": 1, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/topbar.lua": [ + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 2 }, "end": { "line": 79, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 16 }, "end": { "line": 65, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 10 }, "end": { "line": 68, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 18 }, "end": { "line": 64, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 72, "character": 9 }, "end": { "line": 72, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 4 }, "end": { "line": 107, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 86 }, "end": { "line": 108, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/review.lua": [ + { + "severity": 2, + "range": { "start": { "line": 71, "character": 2 }, "end": { "line": 71, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 2 }, "end": { "line": 140, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 192, "character": 2 }, "end": { "line": 192, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 182, "character": 2 }, "end": { "line": 182, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 4 }, "end": { "line": 146, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 4 }, "end": { "line": 147, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 4 }, "end": { "line": 149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 4 }, "end": { "line": 150, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 14 }, "end": { "line": 48, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 19 }, "end": { "line": 49, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 87, "character": 4 }, "end": { "line": 87, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 10 }, "end": { "line": 88, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 17 }, "end": { "line": 110, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 17 }, "end": { "line": 141, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 18 }, "end": { "line": 180, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 22 }, "end": { "line": 181, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 20 }, "end": { "line": 191, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 2 }, "end": { "line": 307, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 22 }, "end": { "line": 76, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 10 }, "end": { "line": 77, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 6 }, "end": { "line": 175, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 19 }, "end": { "line": 19, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 26 }, "end": { "line": 63, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 18 }, "end": { "line": 64, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 6 }, "end": { "line": 66, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 20 }, "end": { "line": 79, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 4 }, "end": { "line": 202, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 4 }, "end": { "line": 292, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 4 }, "end": { "line": 324, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 4 }, "end": { "line": 333, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 8 }, "end": { "line": 38, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 18 }, "end": { "line": 50, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 18 }, "end": { "line": 83, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 9 }, "end": { "line": 91, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 158, "character": 8 }, "end": { "line": 158, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 8 }, "end": { "line": 162, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 163, "character": 8 }, "end": { "line": 163, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 8 }, "end": { "line": 164, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 167, "character": 8 }, "end": { "line": 167, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 8 }, "end": { "line": 168, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 171, "character": 8 }, "end": { "line": 171, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 4 }, "end": { "line": 210, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 312, "character": 23 }, "end": { "line": 312, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 313, "character": 19 }, "end": { "line": 313, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 31 }, "end": { "line": 57, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 24 }, "end": { "line": 155, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 159, "character": 26 }, "end": { "line": 159, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 18 }, "end": { "line": 210, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 6 }, "end": { "line": 224, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 239, "character": 6 }, "end": { "line": 239, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 29 }, "end": { "line": 13, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 16 }, "end": { "line": 101, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 296, "character": 5 }, "end": { "line": 296, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 328, "character": 5 }, "end": { "line": 328, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 29 }, "end": { "line": 93, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 13 }, "end": { "line": 94, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/snapshot.lua": [ + { + "severity": 2, + "range": { "start": { "line": 8, "character": 16 }, "end": { "line": 8, "character": 30 } }, + "message": "Undefined type or alias `vim.SystemOpts`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 217, "character": 2 }, "end": { "line": 217, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 236, "character": 2 }, "end": { "line": 236, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 2 }, "end": { "line": 252, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 9 }, "end": { "line": 61, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 9 }, "end": { "line": 164, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 194, "character": 4 }, "end": { "line": 194, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 4 }, "end": { "line": 30, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 4 }, "end": { "line": 46, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 4 }, "end": { "line": 70, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 4 }, "end": { "line": 91, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 4 }, "end": { "line": 122, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 4 }, "end": { "line": 128, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 133, "character": 4 }, "end": { "line": 133, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 4 }, "end": { "line": 154, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 4 }, "end": { "line": 160, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 4 }, "end": { "line": 210, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 4 }, "end": { "line": 226, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 4 }, "end": { "line": 232, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 4 }, "end": { "line": 242, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 4 }, "end": { "line": 248, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 259, "character": 19 }, "end": { "line": 259, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 11 }, "end": { "line": 20, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 20 }, "end": { "line": 27, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 4 }, "end": { "line": 85, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 14 }, "end": { "line": 138, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 20 }, "end": { "line": 140, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 24 }, "end": { "line": 168, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 171, "character": 20 }, "end": { "line": 171, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 6 }, "end": { "line": 187, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 24 }, "end": { "line": 205, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 214, "character": 4 }, "end": { "line": 214, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 236, "character": 51 }, "end": { "line": 236, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 82 }, "end": { "line": 252, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 17 }, "end": { "line": 18, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 5 }, "end": { "line": 84, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 24 }, "end": { "line": 89, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 6 }, "end": { "line": 191, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 36 }, "end": { "line": 40, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 51 }, "end": { "line": 52, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 80 }, "end": { "line": 57, "character": 83 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 36 }, "end": { "line": 70, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 57 }, "end": { "line": 91, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 36 }, "end": { "line": 122, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 56 }, "end": { "line": 128, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 133, "character": 72 }, "end": { "line": 133, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 36 }, "end": { "line": 154, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 67 }, "end": { "line": 160, "character": 70 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 61 }, "end": { "line": 179, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 6 }, "end": { "line": 212, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 78 }, "end": { "line": 226, "character": 81 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 82 }, "end": { "line": 232, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 78 }, "end": { "line": 242, "character": 81 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 82 }, "end": { "line": 248, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 8 }, "end": { "line": 189, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 44 }, "end": { "line": 89, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 184, "character": 44 }, "end": { "line": 184, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/files.lua": [ + { + "severity": 2, + "range": { "start": { "line": 75, "character": 2 }, "end": { "line": 75, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 5 }, "end": { "line": 32, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 83 }, "end": { "line": 75, "character": 86 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 19 }, "end": { "line": 82, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 14 }, "end": { "line": 83, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 20 }, "end": { "line": 86, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 15 }, "end": { "line": 94, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 11 }, "end": { "line": 136, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 7 }, "end": { "line": 169, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 27 }, "end": { "line": 18, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 18 }, "end": { "line": 62, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 18 }, "end": { "line": 129, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 24 }, "end": { "line": 168, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 16 }, "end": { "line": 19, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 13 }, "end": { "line": 28, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 14 }, "end": { "line": 33, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 13 }, "end": { "line": 36, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 184, "character": 31 }, "end": { "line": 184, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 22 }, "end": { "line": 169, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 54 }, "end": { "line": 169, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 27 }, "end": { "line": 170, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 131, "character": 13 }, "end": { "line": 131, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api.lua": [ + { + "severity": 3, + "range": { "start": { "line": 650, "character": 9 }, "end": { "line": 650, "character": 35 } }, + "message": "Global variable in lowercase initial, Did you miss `local` or misspell it?", + "source": "Lua Diagnostics.", + "code": "lowercase-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 2 }, "end": { "line": 207, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 2 }, "end": { "line": 245, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 2 }, "end": { "line": 260, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 385, "character": 2 }, "end": { "line": 385, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1175, "character": 2 }, "end": { "line": 1175, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 71, "character": 9 }, "end": { "line": 71, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 9 }, "end": { "line": 78, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 4 }, "end": { "line": 201, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 4 }, "end": { "line": 322, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 332, "character": 4 }, "end": { "line": 332, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 4 }, "end": { "line": 342, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 4 }, "end": { "line": 354, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 358, "character": 4 }, "end": { "line": 358, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 363, "character": 4 }, "end": { "line": 363, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 461, "character": 26 }, "end": { "line": 461, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 522, "character": 4 }, "end": { "line": 522, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 542, "character": 4 }, "end": { "line": 542, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 548, "character": 4 }, "end": { "line": 548, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 553, "character": 4 }, "end": { "line": 553, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 575, "character": 4 }, "end": { "line": 575, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 600, "character": 4 }, "end": { "line": 600, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 621, "character": 4 }, "end": { "line": 621, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 627, "character": 4 }, "end": { "line": 627, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 690, "character": 4 }, "end": { "line": 690, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 695, "character": 4 }, "end": { "line": 695, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 740, "character": 4 }, "end": { "line": 740, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1091, "character": 15 }, "end": { "line": 1091, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1104, "character": 4 }, "end": { "line": 1104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1109, "character": 16 }, "end": { "line": 1109, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1149, "character": 4 }, "end": { "line": 1149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 65 }, "end": { "line": 207, "character": 68 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 239, "character": 23 }, "end": { "line": 239, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 24 }, "end": { "line": 245, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 254, "character": 23 }, "end": { "line": 254, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 38 }, "end": { "line": 255, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 24 }, "end": { "line": 260, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 531, "character": 6 }, "end": { "line": 531, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 567, "character": 6 }, "end": { "line": 567, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 592, "character": 6 }, "end": { "line": 592, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 612, "character": 6 }, "end": { "line": 612, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 641, "character": 6 }, "end": { "line": 641, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 747, "character": 6 }, "end": { "line": 747, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1115, "character": 11 }, "end": { "line": 1115, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1128, "character": 11 }, "end": { "line": 1128, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1134, "character": 11 }, "end": { "line": 1134, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1102, "character": 18 }, "end": { "line": 1102, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1113, "character": 24 }, "end": { "line": 1113, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1150, "character": 6 }, "end": { "line": 1150, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1154, "character": 6 }, "end": { "line": 1154, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 61 }, "end": { "line": 179, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 61 }, "end": { "line": 201, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 57 }, "end": { "line": 322, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 332, "character": 57 }, "end": { "line": 332, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 57 }, "end": { "line": 342, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 47 }, "end": { "line": 354, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 358, "character": 36 }, "end": { "line": 358, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 363, "character": 74 }, "end": { "line": 363, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 522, "character": 36 }, "end": { "line": 522, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 542, "character": 47 }, "end": { "line": 542, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 548, "character": 36 }, "end": { "line": 548, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 553, "character": 68 }, "end": { "line": 553, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 562, "character": 6 }, "end": { "line": 562, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 575, "character": 45 }, "end": { "line": 575, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 582, "character": 6 }, "end": { "line": 582, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 600, "character": 47 }, "end": { "line": 600, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 607, "character": 6 }, "end": { "line": 607, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 621, "character": 44 }, "end": { "line": 621, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 627, "character": 42 }, "end": { "line": 627, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 636, "character": 6 }, "end": { "line": 636, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 690, "character": 44 }, "end": { "line": 690, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 695, "character": 34 }, "end": { "line": 695, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 713, "character": 8 }, "end": { "line": 713, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 729, "character": 8 }, "end": { "line": 729, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 740, "character": 50 }, "end": { "line": 740, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1104, "character": 53 }, "end": { "line": 1104, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 8 }, "end": { "line": 568, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 8 }, "end": { "line": 593, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 8 }, "end": { "line": 613, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 8 }, "end": { "line": 642, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 8 }, "end": { "line": 748, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 776, "character": 8 }, "end": { "line": 776, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 845, "character": 8 }, "end": { "line": 845, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 875, "character": 8 }, "end": { "line": 875, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 909, "character": 8 }, "end": { "line": 909, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 922, "character": 8 }, "end": { "line": 922, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 931, "character": 8 }, "end": { "line": 931, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 954, "character": 8 }, "end": { "line": 954, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 969, "character": 8 }, "end": { "line": 969, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 981, "character": 8 }, "end": { "line": 981, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 993, "character": 8 }, "end": { "line": 993, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 996, "character": 31 }, "end": { "line": 996, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1023, "character": 8 }, "end": { "line": 1023, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 708, "character": 8 }, "end": { "line": 708, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 724, "character": 8 }, "end": { "line": 724, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1116, "character": 13 }, "end": { "line": 1116, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1129, "character": 13 }, "end": { "line": 1129, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1135, "character": 13 }, "end": { "line": 1135, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1152, "character": 8 }, "end": { "line": 1152, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 459, "character": 38 }, "end": { "line": 459, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 8 }, "end": { "line": 563, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 608, "character": 8 }, "end": { "line": 608, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 637, "character": 8 }, "end": { "line": 637, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 10 }, "end": { "line": 714, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 10 }, "end": { "line": 730, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 896, "character": 10 }, "end": { "line": 896, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 906, "character": 10 }, "end": { "line": 906, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 52 }, "end": { "line": 568, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 70 }, "end": { "line": 568, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 50 }, "end": { "line": 593, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 68 }, "end": { "line": 593, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 52 }, "end": { "line": 613, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 70 }, "end": { "line": 613, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 54 }, "end": { "line": 642, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 72 }, "end": { "line": 642, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 56 }, "end": { "line": 748, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 74 }, "end": { "line": 748, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 776, "character": 59 }, "end": { "line": 776, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 845, "character": 73 }, "end": { "line": 845, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 875, "character": 70 }, "end": { "line": 875, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 909, "character": 61 }, "end": { "line": 909, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 922, "character": 43 }, "end": { "line": 922, "character": 46 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 931, "character": 62 }, "end": { "line": 931, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 954, "character": 71 }, "end": { "line": 954, "character": 74 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 969, "character": 38 }, "end": { "line": 969, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 981, "character": 38 }, "end": { "line": 981, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 993, "character": 44 }, "end": { "line": 993, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1023, "character": 76 }, "end": { "line": 1023, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 585, "character": 10 }, "end": { "line": 585, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 587, "character": 10 }, "end": { "line": 587, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 709, "character": 10 }, "end": { "line": 709, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 725, "character": 10 }, "end": { "line": 725, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 819, "character": 35 }, "end": { "line": 819, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 823, "character": 12 }, "end": { "line": 823, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 53 }, "end": { "line": 563, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 584, "character": 10 }, "end": { "line": 584, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 608, "character": 52 }, "end": { "line": 608, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 51 }, "end": { "line": 714, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 69 }, "end": { "line": 714, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 51 }, "end": { "line": 730, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 69 }, "end": { "line": 730, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 896, "character": 86 }, "end": { "line": 896, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 906, "character": 86 }, "end": { "line": 906, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 585, "character": 94 }, "end": { "line": 585, "character": 97 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 587, "character": 60 }, "end": { "line": 587, "character": 63 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 823, "character": 55 }, "end": { "line": 823, "character": 58 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 60 }, "end": { "line": 245, "character": 64 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 66 }, "end": { "line": 245, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 73 }, "end": { "line": 245, "character": 77 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 60 }, "end": { "line": 260, "character": 64 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 66 }, "end": { "line": 260, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 73 }, "end": { "line": 260, "character": 77 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/throttling_emitter.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 4 }, "end": { "line": 53, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/types.lua": [ + { + "severity": 2, + "range": { "start": { "line": 246, "character": 28 }, "end": { "line": 246, "character": 54 } }, + "message": "Undefined type or alias `vim.api.keyset.set_extmark`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 320, "character": 24 }, "end": { "line": 320, "character": 38 } }, + "message": "Undefined type or alias `vim.Diagnostic`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config.lua": [ + { + "severity": 2, + "range": { "start": { "line": 178, "character": 11 }, "end": { "line": 178, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 4 }, "end": { "line": 226, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 13 }, "end": { "line": 247, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 9 }, "end": { "line": 200, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 289, "character": 31 }, "end": { "line": 289, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 98 }, "end": { "line": 226, "character": 101 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 305, "character": 11 }, "end": { "line": 305, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/footer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 85, "character": 2 }, "end": { "line": 85, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 2 }, "end": { "line": 122, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 2 }, "end": { "line": 129, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 2 }, "end": { "line": 140, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 2 }, "end": { "line": 154, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 2 }, "end": { "line": 155, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 2 }, "end": { "line": 156, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 20 }, "end": { "line": 49, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 23 }, "end": { "line": 84, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 8 }, "end": { "line": 114, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 21 }, "end": { "line": 128, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 21 }, "end": { "line": 139, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 12 }, "end": { "line": 62, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 10 }, "end": { "line": 98, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 10 }, "end": { "line": 99, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 113, "character": 8 }, "end": { "line": 113, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 10 }, "end": { "line": 64, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ] + } + }, + "status": "completed", + "time": { "start": 1762176906475, "end": 1762176906837 }, + "title": "lua/opencode/core.lua", + "input": { + "newString": " if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "oldString": " state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)" + }, + "output": "\nThis file has errors, please fix\n\n\n\n" + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "reason": "tool-calls", + "cost": 0, + "id": "prt_a49edb65e001xdiFvgkXFJjIiw", + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "snapshot": "f33f38a70b284207e092c2c578a24e96fdd1bd4d", + "type": "step-finish", + "tokens": { "reasoning": 0, "cache": { "read": 10752, "write": 0 }, "input": 14938, "output": 576 }, + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176899112 }, + "id": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 10752, "write": 0 }, "input": 14938, "output": 576 }, + "providerID": "github-copilot" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "files": ["/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"], + "messageID": "msg_a49ed9828001mN6CDcWPnGnpHS", + "id": "prt_a49edb681001xxBeFUSvyhS2Eo", + "type": "patch", + "hash": "8e7903714919009004aad8754db0035fb47ecb24", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176899112, "completed": 1762176906882 }, + "id": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 10752, "write": 0 }, "input": 14938, "output": 576 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176899112, "completed": 1762176906890 }, + "id": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 10752, "write": 0 }, "input": 14938, "output": 576 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176899112, "completed": 1762176906891 }, + "id": "msg_a49ed9828001mN6CDcWPnGnpHS", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 10752, "write": 0 }, "input": 14938, "output": 576 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176906900 }, + "id": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 0, "write": 0 }, "input": 0, "output": 0 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "time": { "created": 1762176897494 }, + "summary": { + "diffs": [ + { + "file": "lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 0, + "additions": 4, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + } + ] + }, + "role": "user", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176906932 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Fixing param-type-not-match and need-check-nil in core.lua line 130", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_a49edb95a001BXEIud4AFAjA3v", + "snapshot": "f33f38a70b284207e092c2c578a24e96fdd1bd4d", + "type": "step-start", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "id" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " step" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " the" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " string" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "\n" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": ")/" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, model", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " model" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "end" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " string" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `model", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "model" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " to" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " now" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now.", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176907612 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "." + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_Kwbm6aS5sRCah2asJyUHTyZu", + "id": "prt_a49edc06d001Em2jkf8QmWbwv3", + "tool": "edit", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "state": { "status": "pending" }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_Kwbm6aS5sRCah2asJyUHTyZu", + "id": "prt_a49edc06d001Em2jkf8QmWbwv3", + "tool": "edit", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "state": { + "status": "running", + "input": { + "newString": " params.model = { modelID = model, providerID = provider }", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "oldString": " params.model = { providerID = provider, modelID = model }" + }, + "time": { "start": 1762176909474 } + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "file.edited", + "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua" } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.\n\nNow, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.\n\nCurrently, the code is:\n```lua\nif opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\nend\n```\nThe expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.\n\nI'll fix this now.", + "id": "prt_a49edb95c0019lorS0C7cytxrl", + "time": { "start": 1762176909485, "end": 1762176909485 }, + "type": "text", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_Kwbm6aS5sRCah2asJyUHTyZu", + "id": "prt_a49edc06d001Em2jkf8QmWbwv3", + "tool": "edit", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "state": { + "metadata": { + "filediff": { + "file": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 1, + "additions": 1, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + }, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n+++ /home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua\n@@ -114,9 +114,9 @@\n local params = {}\n \n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n- params.model = { providerID = provider, modelID = model }\n+ params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n \n if opts.agent then\n", + "diagnostics": { + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/autocmds.lua": [ + { + "severity": 2, + "range": { "start": { "line": 11, "character": 2 }, "end": { "line": 11, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 2 }, "end": { "line": 37, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 2 }, "end": { "line": 48, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 2 }, "end": { "line": 58, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 16 }, "end": { "line": 5, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 23 }, "end": { "line": 47, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 9 }, "end": { "line": 16, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 8 }, "end": { "line": 17, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 44 }, "end": { "line": 32, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 31 }, "end": { "line": 33, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 23 }, "end": { "line": 66, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 24 }, "end": { "line": 62, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 59 }, "end": { "line": 35, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 6 }, "end": { "line": 90, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 6 }, "end": { "line": 93, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/commands.lua": [ + { + "severity": 2, + "range": { "start": { "line": 81, "character": 8 }, "end": { "line": 81, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 37 }, "end": { "line": 27, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 10 }, "end": { "line": 75, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 56 }, "end": { "line": 81, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config_file.lua": [ + { + "severity": 2, + "range": { "start": { "line": 17, "character": 4 }, "end": { "line": 17, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 4 }, "end": { "line": 34, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 14 }, "end": { "line": 61, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 11 }, "end": { "line": 90, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 53 }, "end": { "line": 17, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 74 }, "end": { "line": 17, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 54 }, "end": { "line": 34, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 75 }, "end": { "line": 34, "character": 78 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 56 }, "end": { "line": 51, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 77 }, "end": { "line": 51, "character": 80 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/blink_cmp.lua": [ + { + "severity": 2, + "range": { "start": { "line": 24, "character": 9 }, "end": { "line": 24, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 37 }, "end": { "line": 35, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 49 }, "end": { "line": 35, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/nvim_cmp.lua": [ + { + "severity": 2, + "range": { "start": { "line": 83, "character": 18 }, "end": { "line": 83, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 11 }, "end": { "line": 26, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 14 }, "end": { "line": 86, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 39 }, "end": { "line": 34, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 51 }, "end": { "line": 34, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/engines/vim_complete.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 2 }, "end": { "line": 25, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 116, "character": 25 }, "end": { "line": 116, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 21 }, "end": { "line": 20, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 15 }, "end": { "line": 46, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 14 }, "end": { "line": 47, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 15 }, "end": { "line": 64, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 14 }, "end": { "line": 65, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 37 }, "end": { "line": 38, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 9 }, "end": { "line": 85, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 49 }, "end": { "line": 38, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 6 }, "end": { "line": 108, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 6 }, "end": { "line": 9, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 6 }, "end": { "line": 10, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 41 }, "end": { "line": 106, "character": 44 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/context.lua": [ + { + "severity": 2, + "range": { "start": { "line": 286, "character": 2 }, "end": { "line": 286, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 290, "character": 2 }, "end": { "line": 290, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 2 }, "end": { "line": 292, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 297, "character": 2 }, "end": { "line": 297, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 296, "character": 2 }, "end": { "line": 296, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 2 }, "end": { "line": 298, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 2 }, "end": { "line": 299, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 504, "character": 2 }, "end": { "line": 504, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 516, "character": 2 }, "end": { "line": 516, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 21 }, "end": { "line": 58, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 27 }, "end": { "line": 59, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 23 }, "end": { "line": 73, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 22 }, "end": { "line": 78, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 26 }, "end": { "line": 79, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 4 }, "end": { "line": 144, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 18 }, "end": { "line": 218, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 25 }, "end": { "line": 266, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 479, "character": 18 }, "end": { "line": 479, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 45 }, "end": { "line": 28, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 29 }, "end": { "line": 51, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 22 }, "end": { "line": 92, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 115, "character": 29 }, "end": { "line": 115, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 29 }, "end": { "line": 129, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 18 }, "end": { "line": 141, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 142, "character": 17 }, "end": { "line": 142, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 9 }, "end": { "line": 148, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 9 }, "end": { "line": 150, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 9 }, "end": { "line": 156, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 167, "character": 29 }, "end": { "line": 167, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 9 }, "end": { "line": 179, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 182, "character": 29 }, "end": { "line": 182, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 196, "character": 29 }, "end": { "line": 196, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 8 }, "end": { "line": 237, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 15 }, "end": { "line": 249, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 21 }, "end": { "line": 265, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 22 }, "end": { "line": 281, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 282, "character": 18 }, "end": { "line": 282, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 22 }, "end": { "line": 283, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 15 }, "end": { "line": 287, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 21 }, "end": { "line": 291, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 293, "character": 19 }, "end": { "line": 293, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 24 }, "end": { "line": 298, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 312, "character": 19 }, "end": { "line": 312, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 479, "character": 51 }, "end": { "line": 479, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 483, "character": 11 }, "end": { "line": 483, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 492, "character": 11 }, "end": { "line": 492, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 503, "character": 18 }, "end": { "line": 503, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 11 }, "end": { "line": 255, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 256, "character": 16 }, "end": { "line": 256, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 334, "character": 11 }, "end": { "line": 334, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 347, "character": 11 }, "end": { "line": 347, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 357, "character": 11 }, "end": { "line": 357, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 423, "character": 27 }, "end": { "line": 423, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 60 }, "end": { "line": 30, "character": 63 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 34 }, "end": { "line": 83, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 34 }, "end": { "line": 86, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 34 }, "end": { "line": 89, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 31 }, "end": { "line": 250, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 42 }, "end": { "line": 354, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 9 }, "end": { "line": 276, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 34 }, "end": { "line": 266, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 509, "character": 49 }, "end": { "line": 509, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 521, "character": 49 }, "end": { "line": 521, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 38 }, "end": { "line": 141, "character": 42 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 64 }, "end": { "line": 298, "character": 68 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 70 }, "end": { "line": 298, "character": 75 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 77 }, "end": { "line": 298, "character": 81 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 51 }, "end": { "line": 250, "character": 55 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/sort.lua": [ + { + "severity": 2, + "range": { "start": { "line": 31, "character": 40 }, "end": { "line": 31, "character": 43 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 40 }, "end": { "line": 32, "character": 43 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/context_bar.lua": [ + { + "severity": 2, + "range": { "start": { "line": 172, "character": 2 }, "end": { "line": 172, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 2 }, "end": { "line": 162, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 16 }, "end": { "line": 149, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 10 }, "end": { "line": 151, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 18 }, "end": { "line": 148, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 9 }, "end": { "line": 155, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 4 }, "end": { "line": 180, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 7 }, "end": { "line": 98, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 7 }, "end": { "line": 99, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 7 }, "end": { "line": 100, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 20 }, "end": { "line": 175, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/contextual_actions.lua": [ + { + "severity": 2, + "range": { "start": { "line": 22, "character": 2 }, "end": { "line": 22, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 2 }, "end": { "line": 42, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 4 }, "end": { "line": 82, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 16 }, "end": { "line": 19, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 18 }, "end": { "line": 20, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 6 }, "end": { "line": 88, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 6 }, "end": { "line": 26, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 6 }, "end": { "line": 46, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 6 }, "end": { "line": 63, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 8 }, "end": { "line": 31, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 8 }, "end": { "line": 54, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 10 }, "end": { "line": 90, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 25 }, "end": { "line": 27, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 25 }, "end": { "line": 50, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 93, "character": 27 }, "end": { "line": 93, "character": 33 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/debug_helper.lua": [ + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 2 }, "end": { "line": 77, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 2 }, "end": { "line": 16, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 2 }, "end": { "line": 76, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 19 }, "end": { "line": 16, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 4 }, "end": { "line": 20, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 4 }, "end": { "line": 32, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 4 }, "end": { "line": 64, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 4 }, "end": { "line": 70, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 16 }, "end": { "line": 75, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 18 }, "end": { "line": 11, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 19 }, "end": { "line": 12, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 23 }, "end": { "line": 35, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 51 }, "end": { "line": 46, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 19 }, "end": { "line": 74, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 72 }, "end": { "line": 77, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 5 }, "end": { "line": 18, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 46 }, "end": { "line": 32, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 48 }, "end": { "line": 64, "character": 51 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 45 }, "end": { "line": 70, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/diff_tab.lua": [ + { + "severity": 2, + "range": { "start": { "line": 37, "character": 2 }, "end": { "line": 37, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 2 }, "end": { "line": 56, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 24 }, "end": { "line": 35, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 4 }, "end": { "line": 43, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 4 }, "end": { "line": 44, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 10 }, "end": { "line": 14, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 27 }, "end": { "line": 36, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 17 }, "end": { "line": 38, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 18 }, "end": { "line": 54, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 22 }, "end": { "line": 55, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 22 }, "end": { "line": 5, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 10 }, "end": { "line": 6, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 6 }, "end": { "line": 47, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 6 }, "end": { "line": 48, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 20 }, "end": { "line": 7, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 18 }, "end": { "line": 10, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 9 }, "end": { "line": 16, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 50 }, "end": { "line": 35, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 23 }, "end": { "line": 40, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 25 }, "end": { "line": 44, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 23 }, "end": { "line": 51, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 16 }, "end": { "line": 25, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 29 }, "end": { "line": 18, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 13 }, "end": { "line": 19, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/file_picker.lua": [ + { + "severity": 2, + "range": { "start": { "line": 157, "character": 2 }, "end": { "line": 157, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 21 }, "end": { "line": 103, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 22 }, "end": { "line": 104, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 21 }, "end": { "line": 105, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 11 }, "end": { "line": 11, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 6 }, "end": { "line": 130, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 6 }, "end": { "line": 134, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 8 }, "end": { "line": 132, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 26 }, "end": { "line": 126, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua": [ + { + "severity": 2, + "range": { "start": { "line": 400, "character": 43 }, "end": { "line": 400, "character": 57 } }, + "message": "Undefined type or alias `vim.Diagnostic`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 73 }, "end": { "line": 160, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 37 }, "end": { "line": 224, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 310, "character": 16 }, "end": { "line": 310, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 326, "character": 19 }, "end": { "line": 326, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 367, "character": 19 }, "end": { "line": 367, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 384, "character": 19 }, "end": { "line": 384, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 438, "character": 5 }, "end": { "line": 438, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 448, "character": 19 }, "end": { "line": 448, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 16 }, "end": { "line": 563, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 698, "character": 16 }, "end": { "line": 698, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 437, "character": 14 }, "end": { "line": 437, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 6 }, "end": { "line": 21, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 64 }, "end": { "line": 283, "character": 67 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 475, "character": 27 }, "end": { "line": 475, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 484, "character": 30 }, "end": { "line": 484, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 497, "character": 27 }, "end": { "line": 497, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 26 }, "end": { "line": 563, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 700, "character": 12 }, "end": { "line": 700, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 798, "character": 64 }, "end": { "line": 798, "character": 67 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 300, "character": 10 }, "end": { "line": 300, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 571, "character": 19 }, "end": { "line": 571, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 304, "character": 34 }, "end": { "line": 304, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 413, "character": 17 }, "end": { "line": 413, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 655, "character": 23 }, "end": { "line": 655, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 781, "character": 42 }, "end": { "line": 781, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 68 }, "end": { "line": 299, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 769, "character": 38 }, "end": { "line": 769, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 49, "character": 71 }, "end": { "line": 49, "character": 77 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/highlight.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 19 }, "end": { "line": 3, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 4 }, "end": { "line": 6, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 4 }, "end": { "line": 7, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 4 }, "end": { "line": 8, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 4 }, "end": { "line": 9, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 4 }, "end": { "line": 10, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 4 }, "end": { "line": 12, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 4 }, "end": { "line": 15, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 4 }, "end": { "line": 16, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 4 }, "end": { "line": 17, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 4 }, "end": { "line": 20, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 4 }, "end": { "line": 21, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 4 }, "end": { "line": 22, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 4 }, "end": { "line": 23, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 4 }, "end": { "line": 25, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 4 }, "end": { "line": 26, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 4 }, "end": { "line": 27, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 4 }, "end": { "line": 29, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 4 }, "end": { "line": 30, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 4 }, "end": { "line": 32, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 4 }, "end": { "line": 36, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 4 }, "end": { "line": 37, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 4 }, "end": { "line": 38, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 4 }, "end": { "line": 43, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 4 }, "end": { "line": 44, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 4 }, "end": { "line": 46, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 4 }, "end": { "line": 47, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 4 }, "end": { "line": 48, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 4 }, "end": { "line": 49, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 4 }, "end": { "line": 50, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 4 }, "end": { "line": 51, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 4 }, "end": { "line": 53, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 4 }, "end": { "line": 54, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 4 }, "end": { "line": 55, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 4 }, "end": { "line": 58, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 4 }, "end": { "line": 59, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 4 }, "end": { "line": 60, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 4 }, "end": { "line": 61, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/icons.lua": [ + { + "severity": 2, + "range": { "start": { "line": 89, "character": 6 }, "end": { "line": 89, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 8 }, "end": { "line": 90, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 10 }, "end": { "line": 92, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/loading_animation.lua": [ + { + "severity": 2, + "range": { "start": { "line": 28, "character": 11 }, "end": { "line": 28, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 10 }, "end": { "line": 13, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 4 }, "end": { "line": 100, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 28 }, "end": { "line": 45, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 9 }, "end": { "line": 34, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 62 }, "end": { "line": 34, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/mention.lua": [ + { + "severity": 2, + "range": { "start": { "line": 103, "character": 2 }, "end": { "line": 103, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 27 }, "end": { "line": 4, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 2 }, "end": { "line": 104, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 2 }, "end": { "line": 105, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 8 }, "end": { "line": 11, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 6 }, "end": { "line": 33, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 23 }, "end": { "line": 94, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 4 }, "end": { "line": 112, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 32 }, "end": { "line": 14, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 26 }, "end": { "line": 56, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 7 }, "end": { "line": 132, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 7 }, "end": { "line": 137, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 116, "character": 25 }, "end": { "line": 116, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 27 }, "end": { "line": 137, "character": 31 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/navigation.lua": [ + { + "severity": 2, + "range": { "start": { "line": 53, "character": 2 }, "end": { "line": 53, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 2 }, "end": { "line": 86, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 23 }, "end": { "line": 32, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 34, "character": 19 }, "end": { "line": 34, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 21 }, "end": { "line": 52, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 23 }, "end": { "line": 66, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 19 }, "end": { "line": 68, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 6 }, "end": { "line": 47, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 6 }, "end": { "line": 82, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output.lua": [ + { + "severity": 2, + "range": { "start": { "line": 94, "character": 9 }, "end": { "line": 94, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 9 }, "end": { "line": 110, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 30 }, "end": { "line": 141, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 30 }, "end": { "line": 145, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/output_window.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 14 }, "end": { "line": 4, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 2 }, "end": { "line": 39, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 2 }, "end": { "line": 40, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 2 }, "end": { "line": 42, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 43, "character": 2 }, "end": { "line": 43, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 2 }, "end": { "line": 44, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 2 }, "end": { "line": 45, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 2 }, "end": { "line": 47, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 2 }, "end": { "line": 48, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 2 }, "end": { "line": 49, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 2 }, "end": { "line": 50, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 2 }, "end": { "line": 51, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 2 }, "end": { "line": 61, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 2 }, "end": { "line": 89, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 2 }, "end": { "line": 90, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 2 }, "end": { "line": 91, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 2 }, "end": { "line": 106, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 2 }, "end": { "line": 141, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 158, "character": 2 }, "end": { "line": 158, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 2 }, "end": { "line": 168, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 9 }, "end": { "line": 69, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 4 }, "end": { "line": 139, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 8 }, "end": { "line": 148, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 8 }, "end": { "line": 149, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 7, "character": 21 }, "end": { "line": 7, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 22 }, "end": { "line": 58, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 12 }, "end": { "line": 132, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 11 }, "end": { "line": 30, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 6 }, "end": { "line": 162, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 172, "character": 6 }, "end": { "line": 172, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 656, "character": 2 }, "end": { "line": 656, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 733, "character": 2 }, "end": { "line": 733, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 4 }, "end": { "line": 507, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 685, "character": 4 }, "end": { "line": 685, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 699, "character": 4 }, "end": { "line": 699, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 194, "character": 18 }, "end": { "line": 194, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 17 }, "end": { "line": 195, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 21 }, "end": { "line": 197, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 4 }, "end": { "line": 213, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 669, "character": 9 }, "end": { "line": 669, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 31 }, "end": { "line": 189, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 453, "character": 30 }, "end": { "line": 453, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 54 }, "end": { "line": 507, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 507, "character": 73 }, "end": { "line": 507, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 685, "character": 36 }, "end": { "line": 685, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 316, "character": 23 }, "end": { "line": 316, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 9 }, "end": { "line": 30, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 9 }, "end": { "line": 32, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/session_picker.lua": [ + { + "severity": 2, + "range": { "start": { "line": 423, "character": 2 }, "end": { "line": 423, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 16 }, "end": { "line": 233, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 25 }, "end": { "line": 53, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 365, "character": 6 }, "end": { "line": 365, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 396, "character": 6 }, "end": { "line": 396, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 401, "character": 6 }, "end": { "line": 401, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 8 }, "end": { "line": 342, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 10 }, "end": { "line": 324, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 8 }, "end": { "line": 402, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 22 }, "end": { "line": 249, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 51 }, "end": { "line": 402, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 402, "character": 69 }, "end": { "line": 402, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 22 }, "end": { "line": 54, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 253, "character": 20 }, "end": { "line": 253, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 20 }, "end": { "line": 276, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 343, "character": 22 }, "end": { "line": 343, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 24 }, "end": { "line": 107, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/timer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 31, "character": 18 }, "end": { "line": 31, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 16 }, "end": { "line": 25, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 53, "character": 15 }, "end": { "line": 53, "character": 21 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 32, "character": 45 }, "end": { "line": 32, "character": 51 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/helpers.lua": [ + { + "severity": 2, + "range": { "start": { "line": 112, "character": 2 }, "end": { "line": 112, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 2 }, "end": { "line": 64, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 2 }, "end": { "line": 78, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 2 }, "end": { "line": 98, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 109, "character": 26 }, "end": { "line": 109, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 59, "character": 2 }, "end": { "line": 59, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 25 }, "end": { "line": 96, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 9 }, "end": { "line": 65, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 8 }, "end": { "line": 91, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 4 }, "end": { "line": 102, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 157, "character": 9 }, "end": { "line": 157, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 10 }, "end": { "line": 237, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 21 }, "end": { "line": 249, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 19 }, "end": { "line": 45, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 6 }, "end": { "line": 122, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 264, "character": 14 }, "end": { "line": 264, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 20 }, "end": { "line": 39, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 15 }, "end": { "line": 70, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 71, "character": 10 }, "end": { "line": 71, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 7 }, "end": { "line": 86, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 25 }, "end": { "line": 84, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 87, "character": 12 }, "end": { "line": 87, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 12 }, "end": { "line": 262, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 263, "character": 15 }, "end": { "line": 263, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 177, "character": 28 }, "end": { "line": 177, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 17 }, "end": { "line": 180, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 39 }, "end": { "line": 197, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 36 }, "end": { "line": 199, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/manual/init_replay.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 0 }, "end": { "line": 21, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 0 }, "end": { "line": 22, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 0 }, "end": { "line": 23, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 0 }, "end": { "line": 27, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 0 }, "end": { "line": 29, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 0 }, "end": { "line": 19, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 0 }, "end": { "line": 24, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 0, "character": 20 }, "end": { "line": 0, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 2 }, "end": { "line": 16, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 3 }, "end": { "line": 8, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 5 }, "end": { "line": 10, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/manual/renderer_replay.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 2 }, "end": { "line": 35, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 2 }, "end": { "line": 134, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 2 }, "end": { "line": 200, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 2 }, "end": { "line": 205, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 2 }, "end": { "line": 265, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 266, "character": 2 }, "end": { "line": 266, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 284, "character": 2 }, "end": { "line": 284, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 289, "character": 2 }, "end": { "line": 289, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 293, "character": 2 }, "end": { "line": 293, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 2 }, "end": { "line": 298, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 303, "character": 2 }, "end": { "line": 303, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 2 }, "end": { "line": 307, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 311, "character": 2 }, "end": { "line": 311, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 315, "character": 2 }, "end": { "line": 315, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 2 }, "end": { "line": 319, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 331, "character": 2 }, "end": { "line": 331, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 336, "character": 2 }, "end": { "line": 336, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 337, "character": 2 }, "end": { "line": 337, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 338, "character": 2 }, "end": { "line": 338, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 339, "character": 2 }, "end": { "line": 339, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 340, "character": 2 }, "end": { "line": 340, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 341, "character": 2 }, "end": { "line": 341, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 4 }, "end": { "line": 112, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 152, "character": 4 }, "end": { "line": 152, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 4 }, "end": { "line": 181, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 4 }, "end": { "line": 189, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 4 }, "end": { "line": 262, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 4 }, "end": { "line": 356, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 20 }, "end": { "line": 16, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 35, "character": 69 }, "end": { "line": 35, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 6 }, "end": { "line": 70, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 21 }, "end": { "line": 134, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 16 }, "end": { "line": 162, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 163, "character": 19 }, "end": { "line": 163, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 14 }, "end": { "line": 168, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 57 }, "end": { "line": 200, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 4 }, "end": { "line": 207, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 246, "character": 4 }, "end": { "line": 246, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 14 }, "end": { "line": 255, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 256, "character": 15 }, "end": { "line": 256, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 257, "character": 21 }, "end": { "line": 257, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 4 }, "end": { "line": 333, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 27 }, "end": { "line": 26, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 176, "character": 6 }, "end": { "line": 176, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 47 }, "end": { "line": 19, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 61 }, "end": { "line": 28, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 152, "character": 45 }, "end": { "line": 152, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 173, "character": 17 }, "end": { "line": 173, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 49 }, "end": { "line": 181, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 59 }, "end": { "line": 189, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 6 }, "end": { "line": 213, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 54 }, "end": { "line": 258, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 325, "character": 6 }, "end": { "line": 325, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 79 }, "end": { "line": 356, "character": 82 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 6 }, "end": { "line": 48, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 45 }, "end": { "line": 70, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 18 }, "end": { "line": 222, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 21 }, "end": { "line": 223, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 81 }, "end": { "line": 333, "character": 84 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 12 }, "end": { "line": 54, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 12 }, "end": { "line": 55, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 176, "character": 66 }, "end": { "line": 176, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 208, "character": 13 }, "end": { "line": 208, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 8 }, "end": { "line": 51, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 8 }, "end": { "line": 52, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 325, "character": 61 }, "end": { "line": 325, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/init.lua": [ + { + "severity": 2, + "range": { "start": { "line": 32, "character": 0 }, "end": { "line": 32, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 0 }, "end": { "line": 9, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 0 }, "end": { "line": 26, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 27 }, "end": { "line": 4, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 24 }, "end": { "line": 5, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 20 }, "end": { "line": 8, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 3 }, "end": { "line": 15, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 5 }, "end": { "line": 17, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 2 }, "end": { "line": 12, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 2 }, "end": { "line": 62, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 2 }, "end": { "line": 74, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 2 }, "end": { "line": 80, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 4 }, "end": { "line": 14, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 4 }, "end": { "line": 18, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 4 }, "end": { "line": 63, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 4 }, "end": { "line": 64, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 24 }, "end": { "line": 13, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 22 }, "end": { "line": 17, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 4 }, "end": { "line": 28, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 4 }, "end": { "line": 65, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 26 }, "end": { "line": 27, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 4, "character": 2 }, "end": { "line": 4, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 2 }, "end": { "line": 15, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 2 }, "end": { "line": 60, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 7, "character": 0 }, "end": { "line": 7, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 2 }, "end": { "line": 26, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 2 }, "end": { "line": 114, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 2 }, "end": { "line": 144, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 4 }, "end": { "line": 31, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 4 }, "end": { "line": 66, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 4 }, "end": { "line": 86, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 115, "character": 4 }, "end": { "line": 115, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 4 }, "end": { "line": 155, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 165, "character": 4 }, "end": { "line": 165, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 4 }, "end": { "line": 175, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 4 }, "end": { "line": 187, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 198, "character": 4 }, "end": { "line": 198, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 4 }, "end": { "line": 205, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 9 }, "end": { "line": 12, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 6 }, "end": { "line": 90, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 22 }, "end": { "line": 94, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 31 }, "end": { "line": 199, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 66 }, "end": { "line": 201, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 2 }, "end": { "line": 18, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 51, "character": 2 }, "end": { "line": 51, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 2 }, "end": { "line": 66, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 2 }, "end": { "line": 81, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 4 }, "end": { "line": 10, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 4 }, "end": { "line": 19, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 24 }, "end": { "line": 9, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 16 }, "end": { "line": 61, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 16 }, "end": { "line": 62, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 16 }, "end": { "line": 63, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 16 }, "end": { "line": 76, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 17 }, "end": { "line": 77, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 16 }, "end": { "line": 78, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 23 }, "end": { "line": 35, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 23 }, "end": { "line": 59, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 67, "character": 23 }, "end": { "line": 74, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 23 }, "end": { "line": 90, "character": 5 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 2 }, "end": { "line": 10, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 2 }, "end": { "line": 26, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 22 }, "end": { "line": 11, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 20 }, "end": { "line": 13, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 0 }, "end": { "line": 6, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 2 }, "end": { "line": 32, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 109, "character": 2 }, "end": { "line": 109, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 2 }, "end": { "line": 232, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 4 }, "end": { "line": 21, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 4 }, "end": { "line": 65, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 4 }, "end": { "line": 81, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 4 }, "end": { "line": 90, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 4 }, "end": { "line": 106, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 4 }, "end": { "line": 110, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 4 }, "end": { "line": 121, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 137, "character": 4 }, "end": { "line": 137, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 157, "character": 4 }, "end": { "line": 157, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 177, "character": 4 }, "end": { "line": 177, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 198, "character": 4 }, "end": { "line": 198, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 211, "character": 4 }, "end": { "line": 211, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 4 }, "end": { "line": 223, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 4 }, "end": { "line": 233, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 24 }, "end": { "line": 37, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 32 }, "end": { "line": 41, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 18 }, "end": { "line": 42, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 4 }, "end": { "line": 69, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 4 }, "end": { "line": 73, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 4 }, "end": { "line": 77, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 4 }, "end": { "line": 102, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 4 }, "end": { "line": 103, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 4 }, "end": { "line": 104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 32 }, "end": { "line": 38, "character": 35 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 36 }, "end": { "line": 39, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 36 }, "end": { "line": 40, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 6 }, "end": { "line": 212, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/util.lua": [ + { + "severity": 2, + "range": { "start": { "line": 61, "character": 9 }, "end": { "line": 61, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 18 }, "end": { "line": 15, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 16 }, "end": { "line": 27, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 16 }, "end": { "line": 281, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 414, "character": 9 }, "end": { "line": 414, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 419, "character": 9 }, "end": { "line": 419, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 450, "character": 24 }, "end": { "line": 450, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 19 }, "end": { "line": 14, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 19 }, "end": { "line": 16, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 253, "character": 25 }, "end": { "line": 253, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 404, "character": 20 }, "end": { "line": 404, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 237, "character": 12 }, "end": { "line": 237, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 28 }, "end": { "line": 322, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 16 }, "end": { "line": 247, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 21 }, "end": { "line": 319, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 343, "character": 20 }, "end": { "line": 343, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 203, "character": 27 }, "end": { "line": 203, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 37 }, "end": { "line": 255, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 31 }, "end": { "line": 258, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 41 }, "end": { "line": 319, "character": 49 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 1.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 282, "character": 7 }, "end": { "line": 282, "character": 13 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 238, "character": 11 }, "end": { "line": 238, "character": 17 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/state.lua": [ + { + "severity": 2, + "range": { "start": { "line": 133, "character": 2 }, "end": { "line": 133, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 161, "character": 14 }, "end": { "line": 161, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 174, "character": 14 }, "end": { "line": 174, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 11 }, "end": { "line": 195, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 10 }, "end": { "line": 138, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 10 }, "end": { "line": 187, "character": 12 } }, + "message": "Missing required fields in type `OpencodeState`: `input_content`, `is_opencode_focused`, `current_mode`, `last_output`, `restore_points`, `cost`, `tokens_count`, `job_count`, `api_client`, `required_version`, `append`, `remove`, `subscribe`, `subscribe`, `unsubscribe`, `is_running`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 3, "character": 0 }, "end": { "line": 3, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 8, "character": 2 }, "end": { "line": 8, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 2 }, "end": { "line": 13, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 2 }, "end": { "line": 46, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 2 }, "end": { "line": 114, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 4 }, "end": { "line": 9, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 4 }, "end": { "line": 68, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 117, "character": 4 }, "end": { "line": 117, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 22 }, "end": { "line": 6, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 4 }, "end": { "line": 108, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 4 }, "end": { "line": 156, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 6 }, "end": { "line": 26, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/session.lua": [ + { + "severity": 2, + "range": { "start": { "line": 54, "character": 9 }, "end": { "line": 54, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 4 }, "end": { "line": 11, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 4 }, "end": { "line": 50, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 24 }, "end": { "line": 114, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 15 }, "end": { "line": 20, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 15 }, "end": { "line": 28, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 15 }, "end": { "line": 36, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 15 }, "end": { "line": 94, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 135, "character": 24 }, "end": { "line": 135, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 33 }, "end": { "line": 145, "character": 36 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 69 }, "end": { "line": 11, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 51 }, "end": { "line": 50, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 72 }, "end": { "line": 50, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 72, "character": 17 }, "end": { "line": 72, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 31 }, "end": { "line": 144, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 178, "character": 50 }, "end": { "line": 178, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 31 }, "end": { "line": 95, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 46 }, "end": { "line": 95, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/context.lua": [ + { + "severity": 2, + "range": { "start": { "line": 206, "character": 2 }, "end": { "line": 206, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 2 }, "end": { "line": 224, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 246, "character": 4 }, "end": { "line": 246, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 4 }, "end": { "line": 247, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 4 }, "end": { "line": 248, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 288, "character": 4 }, "end": { "line": 288, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 24 }, "end": { "line": 74, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 18 }, "end": { "line": 206, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 209, "character": 15 }, "end": { "line": 209, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 4 }, "end": { "line": 218, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 18 }, "end": { "line": 224, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 12 }, "end": { "line": 50, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 12 }, "end": { "line": 53, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 12 }, "end": { "line": 56, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 24 }, "end": { "line": 265, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 270, "character": 7 }, "end": { "line": 270, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 208, "character": 24 }, "end": { "line": 208, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 23 }, "end": { "line": 106, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 251, "character": 14 }, "end": { "line": 251, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 26 }, "end": { "line": 267, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 59 }, "end": { "line": 96, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 37 }, "end": { "line": 218, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 18 }, "end": { "line": 206, "character": 76 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 78 }, "end": { "line": 206, "character": 81 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 18 }, "end": { "line": 224, "character": 72 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 74 }, "end": { "line": 224, "character": 77 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 58 }, "end": { "line": 206, "character": 62 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 64 }, "end": { "line": 206, "character": 69 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 71 }, "end": { "line": 206, "character": 75 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 54 }, "end": { "line": 224, "character": 58 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 60 }, "end": { "line": 224, "character": 65 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 67 }, "end": { "line": 224, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "data": { "versions": ["Lua 5.1", "LuaJIT"] }, + "tags": [2], + "range": { "start": { "line": 208, "character": 17 }, "end": { "line": 208, "character": 23 } }, + "message": "Deprecated.(Defined in Lua 5.1/LuaJIT, current is Lua 5.4.)", + "source": "Lua Diagnostics.", + "code": "deprecated" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/input_window.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 2 }, "end": { "line": 6, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 2 }, "end": { "line": 54, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 55, "character": 2 }, "end": { "line": 55, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 2 }, "end": { "line": 95, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 97, "character": 2 }, "end": { "line": 97, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 2 }, "end": { "line": 98, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 2 }, "end": { "line": 100, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 2 }, "end": { "line": 101, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 2 }, "end": { "line": 104, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 2 }, "end": { "line": 105, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 2 }, "end": { "line": 106, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 2 }, "end": { "line": 126, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 2 }, "end": { "line": 170, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 2 }, "end": { "line": 179, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 197, "character": 2 }, "end": { "line": 197, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 206, "character": 2 }, "end": { "line": 206, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 225, "character": 2 }, "end": { "line": 225, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 2 }, "end": { "line": 245, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 2 }, "end": { "line": 255, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 8 }, "end": { "line": 44, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 8 }, "end": { "line": 45, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 16 }, "end": { "line": 81, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 22 }, "end": { "line": 83, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 4 }, "end": { "line": 90, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 195, "character": 50 }, "end": { "line": 195, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 20 }, "end": { "line": 5, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 22 }, "end": { "line": 22, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 22 }, "end": { "line": 121, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 23 }, "end": { "line": 122, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 4 }, "end": { "line": 146, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 4 }, "end": { "line": 162, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 54 }, "end": { "line": 170, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 16 }, "end": { "line": 181, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 215, "character": 16 }, "end": { "line": 215, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 20 }, "end": { "line": 218, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 235, "character": 16 }, "end": { "line": 235, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 19 }, "end": { "line": 88, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 31, "character": 11 }, "end": { "line": 31, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 37 }, "end": { "line": 53, "character": 40 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 90, "character": 43 }, "end": { "line": 90, "character": 46 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 135, "character": 18 }, "end": { "line": 135, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 18 }, "end": { "line": 138, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 22 }, "end": { "line": 139, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 56 }, "end": { "line": 162, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 26 }, "end": { "line": 258, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/ui.lua": [ + { + "severity": 2, + "range": { "start": { "line": 234, "character": 2 }, "end": { "line": 234, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 2 }, "end": { "line": 68, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 73, "character": 2 }, "end": { "line": 73, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 2 }, "end": { "line": 74, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 117, "character": 2 }, "end": { "line": 117, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 2 }, "end": { "line": 136, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 8 }, "end": { "line": 22, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 8 }, "end": { "line": 23, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 8 }, "end": { "line": 25, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 26, "character": 8 }, "end": { "line": 26, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 8 }, "end": { "line": 27, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 8 }, "end": { "line": 28, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 54, "character": 4 }, "end": { "line": 54, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 58, "character": 9 }, "end": { "line": 58, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 2 }, "end": { "line": 80, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 4 }, "end": { "line": 39, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 22 }, "end": { "line": 147, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 16 }, "end": { "line": 164, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 4 }, "end": { "line": 191, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 218, "character": 22 }, "end": { "line": 218, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 18 }, "end": { "line": 38, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 120, "character": 10 }, "end": { "line": 120, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 10 }, "end": { "line": 139, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 42 }, "end": { "line": 85, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 29 }, "end": { "line": 86, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 5 }, "end": { "line": 122, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 124, "character": 6 }, "end": { "line": 124, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 123, "character": 7 }, "end": { "line": 123, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/git_review.lua": [ + { + "severity": 2, + "range": { "start": { "line": 10, "character": 16 }, "end": { "line": 10, "character": 30 } }, + "message": "Undefined type or alias `vim.SystemOpts`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 2 }, "end": { "line": 19, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 2 }, "end": { "line": 107, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 356, "character": 2 }, "end": { "line": 356, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 4 }, "end": { "line": 15, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 22, "character": 11 }, "end": { "line": 22, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 106, "character": 20 }, "end": { "line": 106, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 2 }, "end": { "line": 292, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 17 }, "end": { "line": 20, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 2 }, "end": { "line": 260, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 4 }, "end": { "line": 128, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 4 }, "end": { "line": 141, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 6 }, "end": { "line": 224, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 233, "character": 6 }, "end": { "line": 233, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 234, "character": 6 }, "end": { "line": 234, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 4 }, "end": { "line": 249, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 261, "character": 4 }, "end": { "line": 261, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 283, "character": 4 }, "end": { "line": 283, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 4 }, "end": { "line": 299, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 318, "character": 4 }, "end": { "line": 318, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 373, "character": 6 }, "end": { "line": 373, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 375, "character": 6 }, "end": { "line": 375, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 8 }, "end": { "line": 68, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 8 }, "end": { "line": 74, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 80, "character": 8 }, "end": { "line": 80, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 4 }, "end": { "line": 149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 25 }, "end": { "line": 212, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 213, "character": 21 }, "end": { "line": 213, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 310, "character": 4 }, "end": { "line": 310, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 49 }, "end": { "line": 78, "character": 52 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 6 }, "end": { "line": 150, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 172, "character": 6 }, "end": { "line": 172, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 190, "character": 6 }, "end": { "line": 190, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 6 }, "end": { "line": 255, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 329, "character": 4 }, "end": { "line": 329, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 271, "character": 8 }, "end": { "line": 271, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 305, "character": 6 }, "end": { "line": 305, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 6 }, "end": { "line": 324, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 330, "character": 6 }, "end": { "line": 330, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 373, "character": 72 }, "end": { "line": 373, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 375, "character": 76 }, "end": { "line": 375, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 29 }, "end": { "line": 40, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 228, "character": 7 }, "end": { "line": 228, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 5 }, "end": { "line": 287, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 13 }, "end": { "line": 262, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 340, "character": 10 }, "end": { "line": 340, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 15 }, "end": { "line": 151, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 331, "character": 15 }, "end": { "line": 331, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/server_job.lua": [ + { + "severity": 2, + "range": { "start": { "line": 100, "character": 12 }, "end": { "line": 100, "character": 15 } }, + "message": "Undefined type or alias `Job`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 35 }, "end": { "line": 11, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 6 }, "end": { "line": 114, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 8 }, "end": { "line": 56, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 25 }, "end": { "line": 64, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 8 }, "end": { "line": 118, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 124, "character": 25 }, "end": { "line": 124, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 51 }, "end": { "line": 114, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 69 }, "end": { "line": 114, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 10 }, "end": { "line": 57, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 76 }, "end": { "line": 118, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 12 }, "end": { "line": 39, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 12 }, "end": { "line": 46, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 62 }, "end": { "line": 57, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 14 }, "end": { "line": 40, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 14 }, "end": { "line": 47, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 72 }, "end": { "line": 40, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 47, "character": 66 }, "end": { "line": 47, "character": 69 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 21, "character": 0 }, "end": { "line": 21, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 2 }, "end": { "line": 24, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 92, "character": 2 }, "end": { "line": 92, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 100, "character": 2 }, "end": { "line": 100, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 111, "character": 2 }, "end": { "line": 111, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 2 }, "end": { "line": 242, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 421, "character": 2 }, "end": { "line": 421, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 486, "character": 2 }, "end": { "line": 486, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 4 }, "end": { "line": 114, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 4 }, "end": { "line": 119, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 4 }, "end": { "line": 129, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 4 }, "end": { "line": 162, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 4 }, "end": { "line": 180, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 4 }, "end": { "line": 199, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 4 }, "end": { "line": 212, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 4 }, "end": { "line": 245, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 274, "character": 4 }, "end": { "line": 274, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 280, "character": 4 }, "end": { "line": 280, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 287, "character": 4 }, "end": { "line": 287, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 306, "character": 4 }, "end": { "line": 306, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 338, "character": 4 }, "end": { "line": 338, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 370, "character": 4 }, "end": { "line": 370, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 396, "character": 4 }, "end": { "line": 396, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 422, "character": 4 }, "end": { "line": 422, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 446, "character": 4 }, "end": { "line": 446, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 470, "character": 4 }, "end": { "line": 470, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 487, "character": 4 }, "end": { "line": 487, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 509, "character": 4 }, "end": { "line": 509, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 6 }, "end": { "line": 249, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 6 }, "end": { "line": 258, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 269, "character": 6 }, "end": { "line": 269, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 6 }, "end": { "line": 250, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 251, "character": 6 }, "end": { "line": 251, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 259, "character": 6 }, "end": { "line": 259, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 262, "character": 6 }, "end": { "line": 262, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 265, "character": 6 }, "end": { "line": 265, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 15 }, "end": { "line": 249, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 16 }, "end": { "line": 258, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 173, "character": 30 }, "end": { "line": 173, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 188, "character": 29 }, "end": { "line": 188, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 526, "character": 11 }, "end": { "line": 526, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 29, "character": 0 }, "end": { "line": 29, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 0 }, "end": { "line": 40, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 0 }, "end": { "line": 50, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 0 }, "end": { "line": 94, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 111, "character": 0 }, "end": { "line": 111, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 23, "character": 2 }, "end": { "line": 23, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 2 }, "end": { "line": 45, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 2 }, "end": { "line": 52, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 2 }, "end": { "line": 65, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 69, "character": 2 }, "end": { "line": 69, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 2 }, "end": { "line": 75, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 95, "character": 2 }, "end": { "line": 95, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 112, "character": 2 }, "end": { "line": 112, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 118, "character": 2 }, "end": { "line": 118, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 125, "character": 2 }, "end": { "line": 125, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 132, "character": 2 }, "end": { "line": 132, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 2 }, "end": { "line": 136, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 4 }, "end": { "line": 119, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 4 }, "end": { "line": 126, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 97, "character": 35 }, "end": { "line": 97, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 42 }, "end": { "line": 104, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 54 }, "end": { "line": 105, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 17 }, "end": { "line": 12, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua": [ + { + "severity": 3, + "range": { "start": { "line": 246, "character": 8 }, "end": { "line": 246, "character": 21 } }, + "message": "Global variable in lowercase initial, Did you miss `local` or misspell it?", + "source": "Lua Diagnostics.", + "code": "lowercase-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 0 }, "end": { "line": 30, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 2 }, "end": { "line": 36, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 2 }, "end": { "line": 99, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 2 }, "end": { "line": 130, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 174, "character": 2 }, "end": { "line": 174, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 199, "character": 2 }, "end": { "line": 199, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 263, "character": 2 }, "end": { "line": 263, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 4 }, "end": { "line": 103, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 131, "character": 4 }, "end": { "line": 131, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 144, "character": 4 }, "end": { "line": 144, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 151, "character": 4 }, "end": { "line": 151, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 4 }, "end": { "line": 175, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 4 }, "end": { "line": 200, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 221, "character": 4 }, "end": { "line": 221, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 240, "character": 4 }, "end": { "line": 240, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 278, "character": 4 }, "end": { "line": 278, "character": 15 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 284, "character": 4 }, "end": { "line": 284, "character": 14 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 290, "character": 4 }, "end": { "line": 290, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 297, "character": 4 }, "end": { "line": 297, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 4 }, "end": { "line": 307, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 317, "character": 4 }, "end": { "line": 317, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 22 }, "end": { "line": 38, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 24 }, "end": { "line": 40, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 4 }, "end": { "line": 42, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 104, "character": 4 }, "end": { "line": 104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 39, "character": 26 }, "end": { "line": 39, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 37, "character": 21 }, "end": { "line": 37, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 285, "character": 6 }, "end": { "line": 285, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 301, "character": 6 }, "end": { "line": 301, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 311, "character": 6 }, "end": { "line": 311, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 321, "character": 6 }, "end": { "line": 321, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 214, "character": 6 }, "end": { "line": 214, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 279, "character": 24 }, "end": { "line": 279, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 286, "character": 6 }, "end": { "line": 286, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 6 }, "end": { "line": 291, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 6 }, "end": { "line": 298, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 308, "character": 6 }, "end": { "line": 308, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 318, "character": 6 }, "end": { "line": 318, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 280, "character": 28 }, "end": { "line": 280, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 11, "character": 21 }, "end": { "line": 27, "character": 3 } }, + "message": "Missing required fields in type `OpencodeApiClient`: `base_url`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 28 }, "end": { "line": 94, "character": 5 } }, + "message": "Missing required fields in type `OpencodeServer`: `spawn_promise`, `shutdown_promise`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 29 }, "end": { "line": 146, "character": 51 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 29 }, "end": { "line": 202, "character": 45 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 243, "character": 29 }, "end": { "line": 243, "character": 45 } }, + "message": "Missing required fields in type `Session`: `workspace`, `description`, `modified`, `path`, `messages_path`, `parts_path`, `snapshot_path`, `cache_path`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 254, "character": 20 }, "end": { "line": 254, "character": 58 } }, + "message": "Missing required fields in type `OpencodeContextConfig`: `enabled`, `cursor_data`, `diagnostics`, `selection`, `agents`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/curl.lua": [ + { + "severity": 2, + "range": { "start": { "line": 39, "character": 16 }, "end": { "line": 39, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 134, "character": 4 }, "end": { "line": 134, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 81, "character": 16 }, "end": { "line": 81, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 15 }, "end": { "line": 126, "character": 16 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 126, "character": 21 }, "end": { "line": 126, "character": 30 } }, + "message": "This function expects a maximum of 0 argument(s) but instead it is receiving 2.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api_client.lua": [ + { + "severity": 2, + "range": { "start": { "line": 382, "character": 12 }, "end": { "line": 382, "character": 15 } }, + "message": "Undefined type or alias `Job`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 223, "character": 164 }, "end": { "line": 223, "character": 168 } }, + "message": "Undefined type or alias `Part`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 28 }, "end": { "line": 393, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 45 }, "end": { "line": 393, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua": [ + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 148, "character": 118 }, "end": { "line": 148, "character": 118 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 149, "character": 121 }, "end": { "line": 149, "character": 121 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 150, "character": 108 }, "end": { "line": 150, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 151, "character": 108 }, "end": { "line": 151, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 152, "character": 117 }, "end": { "line": 152, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 153, "character": 117 }, "end": { "line": 153, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 154, "character": 112 }, "end": { "line": 154, "character": 112 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 155, "character": 102 }, "end": { "line": 155, "character": 102 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 156, "character": 108 }, "end": { "line": 156, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 157, "character": 108 }, "end": { "line": 157, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 158, "character": 104 }, "end": { "line": 158, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 159, "character": 114 }, "end": { "line": 159, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 160, "character": 114 }, "end": { "line": 160, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 161, "character": 100 }, "end": { "line": 161, "character": 100 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 162, "character": 117 }, "end": { "line": 162, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 163, "character": 110 }, "end": { "line": 163, "character": 110 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 164, "character": 104 }, "end": { "line": 164, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 165, "character": 115 }, "end": { "line": 165, "character": 115 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 166, "character": 109 }, "end": { "line": 166, "character": 109 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 167, "character": 113 }, "end": { "line": 167, "character": 113 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 168, "character": 126 }, "end": { "line": 168, "character": 126 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 181, "character": 118 }, "end": { "line": 181, "character": 118 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 182, "character": 121 }, "end": { "line": 182, "character": 121 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 183, "character": 108 }, "end": { "line": 183, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 184, "character": 108 }, "end": { "line": 184, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 185, "character": 117 }, "end": { "line": 185, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 186, "character": 117 }, "end": { "line": 186, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 187, "character": 112 }, "end": { "line": 187, "character": 112 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 188, "character": 102 }, "end": { "line": 188, "character": 102 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 189, "character": 108 }, "end": { "line": 189, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 190, "character": 108 }, "end": { "line": 190, "character": 108 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 191, "character": 104 }, "end": { "line": 191, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 192, "character": 114 }, "end": { "line": 192, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 193, "character": 114 }, "end": { "line": 193, "character": 114 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 194, "character": 100 }, "end": { "line": 194, "character": 100 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 195, "character": 117 }, "end": { "line": 195, "character": 117 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 196, "character": 110 }, "end": { "line": 196, "character": 110 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 197, "character": 104 }, "end": { "line": 197, "character": 104 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 198, "character": 115 }, "end": { "line": 198, "character": 115 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 199, "character": 109 }, "end": { "line": 199, "character": 109 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 200, "character": 113 }, "end": { "line": 200, "character": 113 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "data": "syntax", + "range": { "start": { "line": 201, "character": 126 }, "end": { "line": 201, "character": 126 } }, + "message": "`)` expected.", + "source": "Lua Syntax Check.", + "code": "luadoc-miss-symbol" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 39 }, "end": { "line": 281, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 308, "character": 10 }, "end": { "line": 308, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 361, "character": 8 }, "end": { "line": 361, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 2 }, "end": { "line": 9, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 2 }, "end": { "line": 15, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 2 }, "end": { "line": 21, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 2 }, "end": { "line": 41, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 2 }, "end": { "line": 64, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 2 }, "end": { "line": 77, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 2 }, "end": { "line": 93, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 102, "character": 2 }, "end": { "line": 102, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 113, "character": 2 }, "end": { "line": 113, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 56, "character": 4 }, "end": { "line": 56, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 0 }, "end": { "line": 2, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 3, "character": 2 }, "end": { "line": 3, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 2 }, "end": { "line": 14, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 25, "character": 2 }, "end": { "line": 25, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 2 }, "end": { "line": 30, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 2 }, "end": { "line": 36, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 2 }, "end": { "line": 49, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 60, "character": 2 }, "end": { "line": 60, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 9, "character": 19 }, "end": { "line": 9, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 10, "character": 19 }, "end": { "line": 10, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 19 }, "end": { "line": 20, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 19 }, "end": { "line": 21, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 2 }, "end": { "line": 6, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 12, "character": 2 }, "end": { "line": 12, "character": 4 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua": [ + { + "severity": 2, + "range": { "start": { "line": 339, "character": 2 }, "end": { "line": 339, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 16, "character": 28 }, "end": { "line": 16, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 4 }, "end": { "line": 101, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 17 }, "end": { "line": 107, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 4 }, "end": { "line": 130, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 28 }, "end": { "line": 169, "character": 31 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 250, "character": 4 }, "end": { "line": 250, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 4 }, "end": { "line": 267, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 272, "character": 4 }, "end": { "line": 272, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 4 }, "end": { "line": 291, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 299, "character": 4 }, "end": { "line": 299, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 6 }, "end": { "line": 145, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 298, "character": 9 }, "end": { "line": 298, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 6 }, "end": { "line": 61, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 249, "character": 5 }, "end": { "line": 249, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 6 }, "end": { "line": 319, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 59 }, "end": { "line": 101, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 130, "character": 55 }, "end": { "line": 130, "character": 58 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 6 }, "end": { "line": 202, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 6 }, "end": { "line": 252, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 267, "character": 97 }, "end": { "line": 267, "character": 100 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 274, "character": 6 }, "end": { "line": 274, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 291, "character": 39 }, "end": { "line": 291, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 301, "character": 6 }, "end": { "line": 301, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 326, "character": 24 }, "end": { "line": 326, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 57 }, "end": { "line": 145, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 75 }, "end": { "line": 145, "character": 78 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 8 }, "end": { "line": 222, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 8 }, "end": { "line": 226, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 258, "character": 19 }, "end": { "line": 258, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 70 }, "end": { "line": 61, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 6 }, "end": { "line": 156, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 319, "character": 46 }, "end": { "line": 319, "character": 49 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 62 }, "end": { "line": 202, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 222, "character": 38 }, "end": { "line": 222, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 51 }, "end": { "line": 156, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 69 }, "end": { "line": 156, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 59 }, "end": { "line": 136, "character": 64 } }, + "message": "Undefined field `parts`.", + "source": "Lua Diagnostics.", + "code": "undefined-field" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 38 }, "end": { "line": 136, "character": 42 } }, + "message": "Undefined field `info`.", + "source": "Lua Diagnostics.", + "code": "undefined-field" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/health.lua": [ + { + "severity": 2, + "range": { "start": { "line": 2, "character": 15 }, "end": { "line": 2, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 6, "character": 9 }, "end": { "line": 6, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 9 }, "end": { "line": 114, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 14 }, "end": { "line": 154, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 14, "character": 17 }, "end": { "line": 14, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 148, "character": 5 }, "end": { "line": 148, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 18 }, "end": { "line": 147, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/history.lua": [ + { + "severity": 2, + "range": { "start": { "line": 9, "character": 29 }, "end": { "line": 9, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/id.lua": [ + { + "severity": 2, + "range": { "start": { "line": 139, "character": 9 }, "end": { "line": 139, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 9 }, "end": { "line": 101, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 119, "character": 11 }, "end": { "line": 119, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 39 }, "end": { "line": 66, "character": 42 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/keymap.lua": [ + { + "severity": 2, + "range": { "start": { "line": 38, "character": 9 }, "end": { "line": 38, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 9 }, "end": { "line": 49, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 15, "character": 19 }, "end": { "line": 15, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 8 }, "end": { "line": 21, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 74, "character": 12 }, "end": { "line": 74, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 12 }, "end": { "line": 75, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 8 }, "end": { "line": 19, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 8 }, "end": { "line": 65, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 98 }, "end": { "line": 21, "character": 101 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/opencode_server.lua": [ + { + "severity": 2, + "range": { "start": { "line": 58, "character": 34 }, "end": { "line": 58, "character": 53 } }, + "message": "Undefined type or alias `vim.SystemCompleted`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 13 }, "end": { "line": 66, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 12 }, "end": { "line": 18, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/provider.lua": [ + { + "severity": 2, + "range": { "start": { "line": 22, "character": 2 }, "end": { "line": 22, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/promise.lua": [ + { + "severity": 2, + "range": { "start": { "line": 53, "character": 10 }, "end": { "line": 53, "character": 15 } }, + "message": "Undefined param `error`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-param" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 77 }, "end": { "line": 5, "character": 78 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 2, "character": 37 }, "end": { "line": 2, "character": 38 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 5, "character": 34 }, "end": { "line": 5, "character": 35 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 2, "character": 62 }, "end": { "line": 2, "character": 63 } }, + "message": "Undefined type or alias `T`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 42, "character": 24 }, "end": { "line": 42, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 25 }, "end": { "line": 62, "character": 28 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 178, "character": 18 }, "end": { "line": 178, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 103, "character": 26 }, "end": { "line": 103, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 27 }, "end": { "line": 150, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua": [ + { + "severity": 2, + "range": { "start": { "line": 6, "character": 0 }, "end": { "line": 6, "character": 8 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 13 } }, + "message": "Undefined global `before_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 2 }, "end": { "line": 38, "character": 12 } }, + "message": "Undefined global `after_each`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 44, "character": 2 }, "end": { "line": 44, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 2 }, "end": { "line": 242, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 359, "character": 2 }, "end": { "line": 359, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 425, "character": 2 }, "end": { "line": 425, "character": 10 } }, + "message": "Undefined global `describe`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 4 }, "end": { "line": 33, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 41, "character": 4 }, "end": { "line": 41, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 45, "character": 4 }, "end": { "line": 45, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 4 }, "end": { "line": 79, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 121, "character": 4 }, "end": { "line": 121, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 243, "character": 4 }, "end": { "line": 243, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 276, "character": 4 }, "end": { "line": 276, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 309, "character": 4 }, "end": { "line": 309, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 4 }, "end": { "line": 342, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 360, "character": 4 }, "end": { "line": 360, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 393, "character": 4 }, "end": { "line": 393, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 426, "character": 4 }, "end": { "line": 426, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 465, "character": 4 }, "end": { "line": 465, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 496, "character": 4 }, "end": { "line": 496, "character": 6 } }, + "message": "Undefined global `it`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 21, "character": 23 }, "end": { "line": 21, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 24, "character": 4 }, "end": { "line": 24, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 26 }, "end": { "line": 20, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 281, "character": 6 }, "end": { "line": 281, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 306, "character": 6 }, "end": { "line": 306, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 365, "character": 6 }, "end": { "line": 365, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 389, "character": 6 }, "end": { "line": 389, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 278, "character": 30 }, "end": { "line": 278, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 362, "character": 30 }, "end": { "line": 362, "character": 33 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 390, "character": 6 }, "end": { "line": 390, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 421, "character": 6 }, "end": { "line": 421, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 461, "character": 6 }, "end": { "line": 461, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 493, "character": 6 }, "end": { "line": 493, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 521, "character": 6 }, "end": { "line": 521, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 456, "character": 21 }, "end": { "line": 456, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 457, "character": 21 }, "end": { "line": 457, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 458, "character": 21 }, "end": { "line": 458, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 295, "character": 19 }, "end": { "line": 295, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 369, "character": 20 }, "end": { "line": 369, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 378, "character": 19 }, "end": { "line": 378, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 394, "character": 20 }, "end": { "line": 394, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 430, "character": 20 }, "end": { "line": 430, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 469, "character": 20 }, "end": { "line": 469, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 500, "character": 20 }, "end": { "line": 500, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 428, "character": 33 }, "end": { "line": 428, "character": 48 } }, + "message": "Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + }, + { + "severity": 2, + "range": { "start": { "line": 467, "character": 33 }, "end": { "line": 467, "character": 48 } }, + "message": "Missing required fields in type `OpencodePermission`: `type`, `pattern`, `sessionID`, `messageID`, `title`, `metadata`, `time`", + "source": "Lua Diagnostics.", + "code": "missing-fields" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/plugin/opencode.lua": [ + { + "severity": 2, + "range": { "start": { "line": 4, "character": 0 }, "end": { "line": 4, "character": 3 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1, "character": 3 }, "end": { "line": 1, "character": 6 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/topbar.lua": [ + { + "severity": 2, + "range": { "start": { "line": 96, "character": 2 }, "end": { "line": 96, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 2 }, "end": { "line": 79, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 65, "character": 16 }, "end": { "line": 65, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 68, "character": 10 }, "end": { "line": 68, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 18 }, "end": { "line": 64, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 72, "character": 9 }, "end": { "line": 72, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 105, "character": 4 }, "end": { "line": 105, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 107, "character": 4 }, "end": { "line": 107, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 108, "character": 86 }, "end": { "line": 108, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/review.lua": [ + { + "severity": 2, + "range": { "start": { "line": 71, "character": 2 }, "end": { "line": 71, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 2 }, "end": { "line": 140, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 192, "character": 2 }, "end": { "line": 192, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 182, "character": 2 }, "end": { "line": 182, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 145, "character": 4 }, "end": { "line": 145, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 146, "character": 4 }, "end": { "line": 146, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 147, "character": 4 }, "end": { "line": 147, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 149, "character": 4 }, "end": { "line": 149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 150, "character": 4 }, "end": { "line": 150, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 48, "character": 14 }, "end": { "line": 48, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 19 }, "end": { "line": 49, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 87, "character": 4 }, "end": { "line": 87, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 88, "character": 10 }, "end": { "line": 88, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 110, "character": 17 }, "end": { "line": 110, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 141, "character": 17 }, "end": { "line": 141, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 180, "character": 18 }, "end": { "line": 180, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 181, "character": 22 }, "end": { "line": 181, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 20 }, "end": { "line": 191, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 307, "character": 2 }, "end": { "line": 307, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 76, "character": 22 }, "end": { "line": 76, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 77, "character": 10 }, "end": { "line": 77, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 175, "character": 6 }, "end": { "line": 175, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 19 }, "end": { "line": 19, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 63, "character": 26 }, "end": { "line": 63, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 18 }, "end": { "line": 64, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 66, "character": 6 }, "end": { "line": 66, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 79, "character": 20 }, "end": { "line": 79, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 202, "character": 4 }, "end": { "line": 202, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 292, "character": 4 }, "end": { "line": 292, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 324, "character": 4 }, "end": { "line": 324, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 333, "character": 4 }, "end": { "line": 333, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 38, "character": 8 }, "end": { "line": 38, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 50, "character": 18 }, "end": { "line": 50, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 18 }, "end": { "line": 83, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 9 }, "end": { "line": 91, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 158, "character": 8 }, "end": { "line": 158, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 162, "character": 8 }, "end": { "line": 162, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 163, "character": 8 }, "end": { "line": 163, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 8 }, "end": { "line": 164, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 167, "character": 8 }, "end": { "line": 167, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 8 }, "end": { "line": 168, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 171, "character": 8 }, "end": { "line": 171, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 4 }, "end": { "line": 210, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 312, "character": 23 }, "end": { "line": 312, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 313, "character": 19 }, "end": { "line": 313, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 31 }, "end": { "line": 57, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 24 }, "end": { "line": 155, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 159, "character": 26 }, "end": { "line": 159, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 18 }, "end": { "line": 210, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 224, "character": 6 }, "end": { "line": 224, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 239, "character": 6 }, "end": { "line": 239, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 29 }, "end": { "line": 13, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 101, "character": 16 }, "end": { "line": 101, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 296, "character": 5 }, "end": { "line": 296, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 328, "character": 5 }, "end": { "line": 328, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 93, "character": 29 }, "end": { "line": 93, "character": 32 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 13 }, "end": { "line": 94, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/snapshot.lua": [ + { + "severity": 2, + "range": { "start": { "line": 8, "character": 16 }, "end": { "line": 8, "character": 30 } }, + "message": "Undefined type or alias `vim.SystemOpts`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 17, "character": 2 }, "end": { "line": 17, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 217, "character": 2 }, "end": { "line": 217, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 236, "character": 2 }, "end": { "line": 236, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 2 }, "end": { "line": 252, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 61, "character": 9 }, "end": { "line": 61, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 164, "character": 9 }, "end": { "line": 164, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 194, "character": 4 }, "end": { "line": 194, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 13, "character": 4 }, "end": { "line": 13, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 30, "character": 4 }, "end": { "line": 30, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 4 }, "end": { "line": 40, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 46, "character": 4 }, "end": { "line": 46, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 4 }, "end": { "line": 52, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 4 }, "end": { "line": 57, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 4 }, "end": { "line": 70, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 4 }, "end": { "line": 91, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 4 }, "end": { "line": 122, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 4 }, "end": { "line": 128, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 133, "character": 4 }, "end": { "line": 133, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 4 }, "end": { "line": 154, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 4 }, "end": { "line": 160, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 210, "character": 4 }, "end": { "line": 210, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 4 }, "end": { "line": 226, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 4 }, "end": { "line": 232, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 4 }, "end": { "line": 242, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 4 }, "end": { "line": 248, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 259, "character": 19 }, "end": { "line": 259, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 20, "character": 11 }, "end": { "line": 20, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 27, "character": 20 }, "end": { "line": 27, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 85, "character": 4 }, "end": { "line": 85, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 138, "character": 14 }, "end": { "line": 138, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 20 }, "end": { "line": 140, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 24 }, "end": { "line": 168, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 171, "character": 20 }, "end": { "line": 171, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 187, "character": 6 }, "end": { "line": 187, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 205, "character": 24 }, "end": { "line": 205, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 214, "character": 4 }, "end": { "line": 214, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 236, "character": 51 }, "end": { "line": 236, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 252, "character": 82 }, "end": { "line": 252, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 17 }, "end": { "line": 18, "character": 20 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 5 }, "end": { "line": 84, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 24 }, "end": { "line": 89, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 191, "character": 6 }, "end": { "line": 191, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 40, "character": 36 }, "end": { "line": 40, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 52, "character": 51 }, "end": { "line": 52, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 57, "character": 80 }, "end": { "line": 57, "character": 83 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 70, "character": 36 }, "end": { "line": 70, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 91, "character": 57 }, "end": { "line": 91, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 36 }, "end": { "line": 122, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 56 }, "end": { "line": 128, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 133, "character": 72 }, "end": { "line": 133, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 36 }, "end": { "line": 154, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 160, "character": 67 }, "end": { "line": 160, "character": 70 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 61 }, "end": { "line": 179, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 212, "character": 6 }, "end": { "line": 212, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 78 }, "end": { "line": 226, "character": 81 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 232, "character": 82 }, "end": { "line": 232, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 242, "character": 78 }, "end": { "line": 242, "character": 81 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 248, "character": 82 }, "end": { "line": 248, "character": 85 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 189, "character": 8 }, "end": { "line": 189, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 89, "character": 44 }, "end": { "line": 89, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 184, "character": 44 }, "end": { "line": 184, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/completion/files.lua": [ + { + "severity": 2, + "range": { "start": { "line": 75, "character": 2 }, "end": { "line": 75, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 32, "character": 5 }, "end": { "line": 32, "character": 8 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 75, "character": 83 }, "end": { "line": 75, "character": 86 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 82, "character": 19 }, "end": { "line": 82, "character": 22 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 83, "character": 14 }, "end": { "line": 83, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 86, "character": 20 }, "end": { "line": 86, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 94, "character": 15 }, "end": { "line": 94, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 136, "character": 11 }, "end": { "line": 136, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 7 }, "end": { "line": 169, "character": 10 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 18, "character": 27 }, "end": { "line": 18, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 18 }, "end": { "line": 62, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 18 }, "end": { "line": 129, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 168, "character": 24 }, "end": { "line": 168, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 19, "character": 16 }, "end": { "line": 19, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 28, "character": 13 }, "end": { "line": 28, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 33, "character": 14 }, "end": { "line": 33, "character": 17 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 36, "character": 13 }, "end": { "line": 36, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 184, "character": 31 }, "end": { "line": 184, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 22 }, "end": { "line": 169, "character": 25 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 169, "character": 54 }, "end": { "line": 169, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 170, "character": 27 }, "end": { "line": 170, "character": 30 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 131, "character": 13 }, "end": { "line": 131, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/api.lua": [ + { + "severity": 3, + "range": { "start": { "line": 650, "character": 9 }, "end": { "line": 650, "character": 35 } }, + "message": "Global variable in lowercase initial, Did you miss `local` or misspell it?", + "source": "Lua Diagnostics.", + "code": "lowercase-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 2 }, "end": { "line": 207, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 2 }, "end": { "line": 245, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 2 }, "end": { "line": 260, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 385, "character": 2 }, "end": { "line": 385, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1175, "character": 2 }, "end": { "line": 1175, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 71, "character": 9 }, "end": { "line": 71, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 78, "character": 9 }, "end": { "line": 78, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 4 }, "end": { "line": 179, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 4 }, "end": { "line": 201, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 4 }, "end": { "line": 322, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 332, "character": 4 }, "end": { "line": 332, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 4 }, "end": { "line": 342, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 4 }, "end": { "line": 354, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 358, "character": 4 }, "end": { "line": 358, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 363, "character": 4 }, "end": { "line": 363, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 461, "character": 26 }, "end": { "line": 461, "character": 29 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 522, "character": 4 }, "end": { "line": 522, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 542, "character": 4 }, "end": { "line": 542, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 548, "character": 4 }, "end": { "line": 548, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 553, "character": 4 }, "end": { "line": 553, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 575, "character": 4 }, "end": { "line": 575, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 600, "character": 4 }, "end": { "line": 600, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 621, "character": 4 }, "end": { "line": 621, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 627, "character": 4 }, "end": { "line": 627, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 690, "character": 4 }, "end": { "line": 690, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 695, "character": 4 }, "end": { "line": 695, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 740, "character": 4 }, "end": { "line": 740, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1091, "character": 15 }, "end": { "line": 1091, "character": 18 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1104, "character": 4 }, "end": { "line": 1104, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1109, "character": 16 }, "end": { "line": 1109, "character": 19 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1149, "character": 4 }, "end": { "line": 1149, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 207, "character": 65 }, "end": { "line": 207, "character": 68 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 239, "character": 23 }, "end": { "line": 239, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 24 }, "end": { "line": 245, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 254, "character": 23 }, "end": { "line": 254, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 255, "character": 38 }, "end": { "line": 255, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 24 }, "end": { "line": 260, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 531, "character": 6 }, "end": { "line": 531, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 567, "character": 6 }, "end": { "line": 567, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 592, "character": 6 }, "end": { "line": 592, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 612, "character": 6 }, "end": { "line": 612, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 641, "character": 6 }, "end": { "line": 641, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 747, "character": 6 }, "end": { "line": 747, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1115, "character": 11 }, "end": { "line": 1115, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1128, "character": 11 }, "end": { "line": 1128, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1134, "character": 11 }, "end": { "line": 1134, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1102, "character": 18 }, "end": { "line": 1102, "character": 21 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1113, "character": 24 }, "end": { "line": 1113, "character": 27 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1150, "character": 6 }, "end": { "line": 1150, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1154, "character": 6 }, "end": { "line": 1154, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 179, "character": 61 }, "end": { "line": 179, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 201, "character": 61 }, "end": { "line": 201, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 322, "character": 57 }, "end": { "line": 322, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 332, "character": 57 }, "end": { "line": 332, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 342, "character": 57 }, "end": { "line": 342, "character": 60 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 354, "character": 47 }, "end": { "line": 354, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 358, "character": 36 }, "end": { "line": 358, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 363, "character": 74 }, "end": { "line": 363, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 522, "character": 36 }, "end": { "line": 522, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 542, "character": 47 }, "end": { "line": 542, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 548, "character": 36 }, "end": { "line": 548, "character": 39 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 553, "character": 68 }, "end": { "line": 553, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 562, "character": 6 }, "end": { "line": 562, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 575, "character": 45 }, "end": { "line": 575, "character": 48 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 582, "character": 6 }, "end": { "line": 582, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 600, "character": 47 }, "end": { "line": 600, "character": 50 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 607, "character": 6 }, "end": { "line": 607, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 621, "character": 44 }, "end": { "line": 621, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 627, "character": 42 }, "end": { "line": 627, "character": 45 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 636, "character": 6 }, "end": { "line": 636, "character": 9 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 690, "character": 44 }, "end": { "line": 690, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 695, "character": 34 }, "end": { "line": 695, "character": 37 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 713, "character": 8 }, "end": { "line": 713, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 729, "character": 8 }, "end": { "line": 729, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 740, "character": 50 }, "end": { "line": 740, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1104, "character": 53 }, "end": { "line": 1104, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 8 }, "end": { "line": 568, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 8 }, "end": { "line": 593, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 8 }, "end": { "line": 613, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 8 }, "end": { "line": 642, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 8 }, "end": { "line": 748, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 776, "character": 8 }, "end": { "line": 776, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 845, "character": 8 }, "end": { "line": 845, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 875, "character": 8 }, "end": { "line": 875, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 909, "character": 8 }, "end": { "line": 909, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 922, "character": 8 }, "end": { "line": 922, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 931, "character": 8 }, "end": { "line": 931, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 954, "character": 8 }, "end": { "line": 954, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 969, "character": 8 }, "end": { "line": 969, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 981, "character": 8 }, "end": { "line": 981, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 993, "character": 8 }, "end": { "line": 993, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 996, "character": 31 }, "end": { "line": 996, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1023, "character": 8 }, "end": { "line": 1023, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 708, "character": 8 }, "end": { "line": 708, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 724, "character": 8 }, "end": { "line": 724, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1116, "character": 13 }, "end": { "line": 1116, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1129, "character": 13 }, "end": { "line": 1129, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1135, "character": 13 }, "end": { "line": 1135, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1152, "character": 8 }, "end": { "line": 1152, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 459, "character": 38 }, "end": { "line": 459, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 8 }, "end": { "line": 563, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 608, "character": 8 }, "end": { "line": 608, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 637, "character": 8 }, "end": { "line": 637, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 10 }, "end": { "line": 714, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 10 }, "end": { "line": 730, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 896, "character": 10 }, "end": { "line": 896, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 906, "character": 10 }, "end": { "line": 906, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 52 }, "end": { "line": 568, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 568, "character": 70 }, "end": { "line": 568, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 50 }, "end": { "line": 593, "character": 53 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 593, "character": 68 }, "end": { "line": 593, "character": 71 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 52 }, "end": { "line": 613, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 613, "character": 70 }, "end": { "line": 613, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 54 }, "end": { "line": 642, "character": 57 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 642, "character": 72 }, "end": { "line": 642, "character": 75 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 56 }, "end": { "line": 748, "character": 59 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 748, "character": 74 }, "end": { "line": 748, "character": 77 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 776, "character": 59 }, "end": { "line": 776, "character": 62 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 845, "character": 73 }, "end": { "line": 845, "character": 76 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 875, "character": 70 }, "end": { "line": 875, "character": 73 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 909, "character": 61 }, "end": { "line": 909, "character": 64 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 922, "character": 43 }, "end": { "line": 922, "character": 46 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 931, "character": 62 }, "end": { "line": 931, "character": 65 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 954, "character": 71 }, "end": { "line": 954, "character": 74 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 969, "character": 38 }, "end": { "line": 969, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 981, "character": 38 }, "end": { "line": 981, "character": 41 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 993, "character": 44 }, "end": { "line": 993, "character": 47 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 1023, "character": 76 }, "end": { "line": 1023, "character": 79 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 585, "character": 10 }, "end": { "line": 585, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 587, "character": 10 }, "end": { "line": 587, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 709, "character": 10 }, "end": { "line": 709, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 725, "character": 10 }, "end": { "line": 725, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 819, "character": 35 }, "end": { "line": 819, "character": 38 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 823, "character": 12 }, "end": { "line": 823, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 563, "character": 53 }, "end": { "line": 563, "character": 56 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 584, "character": 10 }, "end": { "line": 584, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 608, "character": 52 }, "end": { "line": 608, "character": 55 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 51 }, "end": { "line": 714, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 714, "character": 69 }, "end": { "line": 714, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 51 }, "end": { "line": 730, "character": 54 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 730, "character": 69 }, "end": { "line": 730, "character": 72 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 896, "character": 86 }, "end": { "line": 896, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 906, "character": 86 }, "end": { "line": 906, "character": 89 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 585, "character": 94 }, "end": { "line": 585, "character": 97 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 587, "character": 60 }, "end": { "line": 587, "character": 63 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 823, "character": 55 }, "end": { "line": 823, "character": 58 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 60 }, "end": { "line": 245, "character": 64 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 66 }, "end": { "line": 245, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 245, "character": 73 }, "end": { "line": 245, "character": 77 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 60 }, "end": { "line": 260, "character": 64 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 66 }, "end": { "line": 260, "character": 71 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + }, + { + "severity": 2, + "range": { "start": { "line": 260, "character": 73 }, "end": { "line": 260, "character": 77 } }, + "message": "This function expects a maximum of 1 argument(s) but instead it is receiving 4.", + "source": "Lua Diagnostics.", + "code": "redundant-parameter" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/throttling_emitter.lua": [ + { + "severity": 2, + "range": { "start": { "line": 35, "character": 4 }, "end": { "line": 35, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 53, "character": 4 }, "end": { "line": 53, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/types.lua": [ + { + "severity": 2, + "range": { "start": { "line": 246, "character": 28 }, "end": { "line": 246, "character": 54 } }, + "message": "Undefined type or alias `vim.api.keyset.set_extmark`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + }, + { + "severity": 2, + "range": { "start": { "line": 320, "character": 24 }, "end": { "line": 320, "character": 38 } }, + "message": "Undefined type or alias `vim.Diagnostic`.", + "source": "Lua Diagnostics.", + "code": "undefined-doc-name" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/config.lua": [ + { + "severity": 2, + "range": { "start": { "line": 178, "character": 11 }, "end": { "line": 178, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 4 }, "end": { "line": 226, "character": 7 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 247, "character": 13 }, "end": { "line": 247, "character": 16 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 200, "character": 9 }, "end": { "line": 200, "character": 12 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 289, "character": 31 }, "end": { "line": 289, "character": 34 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 226, "character": 98 }, "end": { "line": 226, "character": 101 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 305, "character": 11 }, "end": { "line": 305, "character": 14 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ], + "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/footer.lua": [ + { + "severity": 2, + "range": { "start": { "line": 85, "character": 2 }, "end": { "line": 85, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 122, "character": 2 }, "end": { "line": 122, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 129, "character": 2 }, "end": { "line": 129, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 140, "character": 2 }, "end": { "line": 140, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 154, "character": 2 }, "end": { "line": 154, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 155, "character": 2 }, "end": { "line": 155, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 156, "character": 2 }, "end": { "line": 156, "character": 5 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 49, "character": 20 }, "end": { "line": 49, "character": 23 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 84, "character": 23 }, "end": { "line": 84, "character": 26 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 114, "character": 8 }, "end": { "line": 114, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 128, "character": 21 }, "end": { "line": 128, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 139, "character": 21 }, "end": { "line": 139, "character": 24 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 62, "character": 12 }, "end": { "line": 62, "character": 15 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 98, "character": 10 }, "end": { "line": 98, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 99, "character": 10 }, "end": { "line": 99, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 113, "character": 8 }, "end": { "line": 113, "character": 11 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + }, + { + "severity": 2, + "range": { "start": { "line": 64, "character": 10 }, "end": { "line": 64, "character": 13 } }, + "message": "Undefined global `vim`.", + "source": "Lua Diagnostics.", + "code": "undefined-global" + } + ] + } + }, + "status": "completed", + "time": { "start": 1762176909474, "end": 1762176910374 }, + "title": "lua/opencode/core.lua", + "input": { + "newString": " params.model = { modelID = model, providerID = provider }", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua", + "oldString": " params.model = { providerID = provider, modelID = model }" + }, + "output": "\nThis file has errors, please fix\n\n\n\n" + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "reason": "tool-calls", + "cost": 0, + "id": "prt_a49edc42b001zynaxIfLCTqFF6", + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "snapshot": "4d76a8382f4cd173216ee6d5949f80aeec2ed395", + "type": "step-finish", + "tokens": { "reasoning": 0, "cache": { "read": 15488, "write": 0 }, "input": 15541, "output": 237 }, + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176906900 }, + "id": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15488, "write": 0 }, "input": 15541, "output": 237 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "files": ["/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua"], + "messageID": "msg_a49edb694001syExFxMUW1ik3n", + "id": "prt_a49edc44d001r66NSVANs6OHqr", + "type": "patch", + "hash": "f33f38a70b284207e092c2c578a24e96fdd1bd4d", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176906900, "completed": 1762176910414 }, + "id": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15488, "write": 0 }, "input": 15541, "output": 237 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176906900, "completed": 1762176910418 }, + "id": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15488, "write": 0 }, "input": 15541, "output": 237 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176906900, "completed": 1762176910418 }, + "id": "msg_a49edb694001syExFxMUW1ik3n", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15488, "write": 0 }, "input": 15541, "output": 237 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176910436 }, + "id": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 0, "write": 0 }, "input": 0, "output": 0 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176910449 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Fixing param-type-not-match and need-check-nil in core.lua line 130", + "version": "0.15.30" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "time": { "created": 1762176897494 }, + "summary": { + "diffs": [ + { + "file": "lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 1, + "additions": 5, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + } + ] + }, + "role": "user", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_a49edc83d001bg2hfdA1yYp8Q1", + "snapshot": "4d76a8382f4cd173216ee6d5949f80aeec2ed395", + "type": "step-start", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176911423 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " type" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID =", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176911423 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " =" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176911423 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " to" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176911423 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " test" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test suite using the provided script to verify everything is", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176911423 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " is" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.\n\nNow, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.\n\nI'll run the test suite using the provided script to verify everything is working.", + "id": "prt_a49edc83f001YsFxwe2yt9W3Jz", + "time": { "start": 1762176912551, "end": 1762176912551 }, + "type": "text", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_4NyLmBcZTmbwsJwuN6gfFqP9", + "id": "prt_a49edcc8e001xT4R3ZUgjYzQng", + "tool": "bash", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "state": { + "metadata": { + "output": "\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\n", + "description": "Run all tests to verify code changes" + }, + "status": "running", + "input": { "command": "./run_tests.sh", "description": "Run all tests to verify code changes" }, + "time": { "start": 1762176912550 } + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_4NyLmBcZTmbwsJwuN6gfFqP9", + "id": "prt_a49edcc8e001xT4R3ZUgjYzQng", + "tool": "bash", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "state": { + "metadata": { + "output": "\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\n", + "description": "Run all tests to verify code changes" + }, + "status": "running", + "input": { "command": "./run_tests.sh", "description": "Run all tests to verify code changes" }, + "time": { "start": 1762176912721 } + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "callID": "call_4NyLmBcZTmbwsJwuN6gfFqP9", + "id": "prt_a49edcc8e001xT4R3ZUgjYzQng", + "tool": "bash", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "state": { + "metadata": { + "exit": 0, + "description": "Run all tests to verify code changes", + "output": "\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/unit/keymap_spec.lua\nScheduling: ./tests/unit/opencode_server_spec.lua\nScheduling: ./tests/unit/context_completion_spec.lua\nScheduling: ./tests/unit/config_file_spec.lua\nScheduling: ./tests/unit/init_spec.lua\nScheduling: ./tests/unit/state_spec.lua\nScheduling: ./tests/unit/render_state_spec.lua\nScheduling: ./tests/unit/server_job_spec.lua\nScheduling: ./tests/unit/config_spec.lua\nScheduling: ./tests/unit/context_bar_spec.lua\nScheduling: ./tests/unit/renderer_spec.lua\nScheduling: ./tests/unit/api_client_spec.lua\nScheduling: ./tests/unit/core_spec.lua\nScheduling: ./tests/unit/session_spec.lua\nScheduling: ./tests/unit/event_manager_spec.lua\nScheduling: ./tests/unit/api_spec.lua\nScheduling: ./tests/unit/context_spec.lua\nScheduling: ./tests/unit/timer_spec.lua\nScheduling: ./tests/unit/util_spec.lua\nScheduling: ./tests/unit/snapshot_spec.lua\nScheduling: ./tests/unit/id_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion get_source should return a completion source\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return empty table when trigger char does not match\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return context items when trigger char matches\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include selection items when selections exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include mentioned files when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include subagent items when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should filter items based on input\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should sort items with available items first\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle nil item gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle item without data gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove mentioned file when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove subagent when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove selection when selection item selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should clean up trigger character from current line\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format diagnostics correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format cursor data correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format selection correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle empty context gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle disabled contexts correctly\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with new format configured keys\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server creates a new server object\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_error when stderr is triggered\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup lazily loads config when accessed\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_project returns project\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode has setup function in the public API\t\n\u001b[32mSuccess\u001b[0m\t||\topencode main module can be required without errors\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset creates a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset resets to empty state\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message sets a new message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates line index for message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates existing message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part sets a new part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part updates line index for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part initializes actions array\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns part at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns nil for line without part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns message at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns nil for line without message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id finds part by call ID\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions to part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions with offset\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions clears actions for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets actions at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets all actions from all parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines updates part line positions\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when expanding\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part removes part and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part clears line index for removed part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message removes message and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message clears line index for removed message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message returns false for non-existent message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does nothing when delta is 0\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts content at or after from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts actions with parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does not rebuild index when nothing shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all invalidates index when content shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all exits early when content found before from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data updates part reference\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data does nothing for non-existent part\t\n\t\n\u001b[32mSuccess: \u001b[0m\t37\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job exposes expected public functions\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api rejects on non 2xx\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job stream_api forwards chunks\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job ensure_server spawns a new opencode server only once\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config uses default values when no options are provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config merges user options with defaults\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar setup subscribes to state changes\t\n\t\n\u001b[32mSuccess: \u001b[0m\t9\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create a new client instance\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should remove trailing slash from base_url\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create client using create factory function\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should have all expected API methods\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should construct URLs correctly with query parameters\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nError detected while processing command line:\nopencode command not found - please install and configure opencode before using this plugin\nUnsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open creates windows if they don't exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open handles new session properly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open focuses the appropriate window\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core select_session filters sessions by description and parentID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message sends a message via api_client\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message creates new session when none active\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message persist options in state when sending message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles empty session list\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns the session with matching ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns nil when no session matches the ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil for non-existent directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns decoded JSON content from directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir skips invalid JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when session is nil\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when messages directory does not exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns messages with their parts\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies listeners on key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies wildcard listeners on any key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) can unregister listeners\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) does not notify if value is unchanged\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should create a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should subscribe and emit events\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle multiple subscribers\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should unsubscribe correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should track subscriber count\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should list event names\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle starting and stopping\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should not start multiple times\t\n\t\n\u001b[32mSuccess: \u001b[0m\t8\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nFile not added to context. Could not read.\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message returns nils if message or parts missing\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_message_legacy extracts legacy tags from text\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag extracts content between tags\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag returns nil if tag not found\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message returns a parts array with prompt as first part\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message includes mentioned_files and subagents\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes current_file if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes mentioned_subagents if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a selection\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a subagent\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n:OpencodeOpenInput is deprecated. Use `:Opencode open input` instead\n:OpencodeStop is deprecated. Use `:Opencode cancel` instead\n:OpencodeClose is deprecated. Use `:Opencode close` instead\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api commands table contains the expected commands with proper structure\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup registers the main Opencode command and legacy commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup sets up legacy command functions that route to main command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api Lua API provides callable functions that match commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing works with run_new command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing requires a prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses flat booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses nested dot notation\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses mixed nesting and booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses numbers\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses no prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single agent prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single model prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single context prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses multiple prefixes in order\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args stops parsing at first non-prefix token\t\n\t\n\u001b[32mSuccess: \u001b[0m\t14\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if no active session\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if read-tree fails\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if checkout-index fails\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate ascending session IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate descending message IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate given IDs correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should throw error for invalid given IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate schemas correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should return available prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate IDs with correct length structure\t\n\t\n\u001b[32mSuccess: \u001b[0m\t7\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new creates a new timer with required options\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new stores optional parameters\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a repeating timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a one-shot timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start passes arguments to on_tick function\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick returns false\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick throws an error\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops previous timer before starting new one\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start throws error when timer creation fails\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop stops a running timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop calls on_stop callback when provided\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop does nothing when timer is not running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false when timer is not started\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns true when timer is running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after timer is stopped\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after one-shot timer completes\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests can restart a stopped timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests handles rapid start/stop cycles\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (session)\t\n\t\n\u001b[32mSuccess: \u001b[0m\t60\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Unit tests passed\u001b[0m\n------------------------------------------------\n\u001b[0;32mAll tests passed successfully!\u001b[0m\n" + }, + "status": "completed", + "time": { "start": 1762176912541, "end": 1762176915509 }, + "title": "./run_tests.sh", + "input": { "command": "./run_tests.sh", "description": "Run all tests to verify code changes" }, + "output": "\u001b[0;33mRunning tests for opencode.nvim\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/minimal/plugin_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin loads the plugin without errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.nvim plugin can be set up with custom config\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Minimal tests passed\u001b[0m\n------------------------------------------------\nStarting...Scheduling: ./tests/unit/keymap_spec.lua\nScheduling: ./tests/unit/opencode_server_spec.lua\nScheduling: ./tests/unit/context_completion_spec.lua\nScheduling: ./tests/unit/config_file_spec.lua\nScheduling: ./tests/unit/init_spec.lua\nScheduling: ./tests/unit/state_spec.lua\nScheduling: ./tests/unit/render_state_spec.lua\nScheduling: ./tests/unit/server_job_spec.lua\nScheduling: ./tests/unit/config_spec.lua\nScheduling: ./tests/unit/context_bar_spec.lua\nScheduling: ./tests/unit/renderer_spec.lua\nScheduling: ./tests/unit/api_client_spec.lua\nScheduling: ./tests/unit/core_spec.lua\nScheduling: ./tests/unit/session_spec.lua\nScheduling: ./tests/unit/event_manager_spec.lua\nScheduling: ./tests/unit/api_spec.lua\nScheduling: ./tests/unit/context_spec.lua\nScheduling: ./tests/unit/timer_spec.lua\nScheduling: ./tests/unit/util_spec.lua\nScheduling: ./tests/unit/snapshot_spec.lua\nScheduling: ./tests/unit/id_spec.lua\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion get_source should return a completion source\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return empty table when trigger char does not match\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should return context items when trigger char matches\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include selection items when selections exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include mentioned files when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should include subagent items when they exist\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should filter items based on input\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion complete function should sort items with available items first\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle nil item gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should handle item without data gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove mentioned file when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove subagent when selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should remove selection when selection item selected\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion on_complete function should clean up trigger character from current line\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format diagnostics correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format cursor data correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion formatting functions should format selection correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle empty context gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tcontext completion edge cases should handle disabled contexts correctly\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with new format configured keys\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server creates a new server object\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_error when stderr is triggered\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup lazily loads config when accessed\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t\n\u001b[32mSuccess\u001b[0m\t||\tconfig_file.setup get_opencode_project returns project\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode has setup function in the public API\t\n\u001b[32mSuccess\u001b[0m\t||\topencode main module can be required without errors\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset creates a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState new and reset resets to empty state\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message sets a new message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates line index for message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_message updates existing message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part sets a new part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part updates line index for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState set_part initializes actions array\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns part at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_at_line returns nil for line without part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns message at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_message_at_line returns nil for line without message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id finds part by call ID\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions to part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions adds actions with offset\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions clears actions for part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets actions at line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState actions gets all actions from all parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines updates part line positions\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when expanding\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_lines returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part removes part and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part clears line index for removed part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_part returns false for non-existent part\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message removes message and shifts subsequent content\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message clears line index for removed message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState remove_message returns false for non-existent message\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does nothing when delta is 0\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts content at or after from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all shifts actions with parts\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all does not rebuild index when nothing shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all invalidates index when content shifted\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState shift_all exits early when content found before from_line\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data updates part reference\t\n\u001b[32mSuccess\u001b[0m\t||\tRenderState update_part_data does nothing for non-existent part\t\n\t\n\u001b[32mSuccess: \u001b[0m\t37\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job exposes expected public functions\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job call_api rejects on non 2xx\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job stream_api forwards chunks\t\n\u001b[32mSuccess\u001b[0m\t||\tserver_job ensure_server spawns a new opencode server only once\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config uses default values when no options are provided\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.config merges user options with defaults\t\n\t\n\u001b[32mSuccess: \u001b[0m\t2\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.ui.context_bar setup subscribes to state changes\t\n\t\n\u001b[32mSuccess: \u001b[0m\t9\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create a new client instance\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should remove trailing slash from base_url\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should create client using create factory function\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should have all expected API methods\t\n\u001b[32mSuccess\u001b[0m\t||\tapi_client should construct URLs correctly with query parameters\t\n\t\n\u001b[32mSuccess: \u001b[0m\t5\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nError detected while processing command line:\nopencode command not found - please install and configure opencode before using this plugin\nUnsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open creates windows if they don't exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open handles new session properly\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core open focuses the appropriate window\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core select_session filters sessions by description and parentID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message sends a message via api_client\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message creates new session when none active\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core send_message persist options in state when sending message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_last_workspace_session handles empty session list\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns the session with matching ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_by_name returns nil when no session matches the ID\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil for non-existent directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir returns decoded JSON content from directory\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session read_json_dir skips invalid JSON files\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when session is nil\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns nil when messages directory does not exist\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.session get_messages returns messages with their parts\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies listeners on key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) notifies wildcard listeners on any key change\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) can unregister listeners\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.state (observable) does not notify if value is unchanged\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should create a new instance\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should subscribe and emit events\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle multiple subscribers\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should unsubscribe correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should track subscriber count\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should list event names\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should handle starting and stopping\t\n\u001b[32mSuccess\u001b[0m\t||\tEventManager should not start multiple times\t\n\t\n\u001b[32mSuccess: \u001b[0m\t8\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nFile not added to context. Could not read.\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_opencode_message returns nils if message or parts missing\t\n\u001b[32mSuccess\u001b[0m\t||\textract_from_message_legacy extracts legacy tags from text\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag extracts content between tags\t\n\u001b[32mSuccess\u001b[0m\t||\textract_legacy_tag returns nil if tag not found\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message returns a parts array with prompt as first part\t\n\u001b[32mSuccess\u001b[0m\t||\tformat_message includes mentioned_files and subagents\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes current_file if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tdelta_context removes mentioned_subagents if unchanged\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a selection\t\n\u001b[32mSuccess\u001b[0m\t||\tadd_file/add_selection/add_subagent adds a subagent\t\n\t\n\u001b[32mSuccess: \u001b[0m\t13\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n:OpencodeOpenInput is deprecated. Use `:Opencode open input` instead\n:OpencodeStop is deprecated. Use `:Opencode cancel` instead\n:OpencodeClose is deprecated. Use `:Opencode close` instead\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api commands table contains the expected commands with proper structure\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup registers the main Opencode command and legacy commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api setup sets up legacy command functions that route to main command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api Lua API provides callable functions that match commands\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing works with run_new command\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing requires a prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t\n\t\n\u001b[32mSuccess: \u001b[0m\t11\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses flat booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses nested dot notation\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses mixed nesting and booleans\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args parses numbers\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_dot_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses no prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single agent prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single model prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses single context prefix\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses multiple prefixes in order\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty prompt after prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args handles empty string\t\n\u001b[32mSuccess\u001b[0m\t||\tutil.parse_run_args stops parsing at first non-prefix token\t\n\t\n\u001b[32mSuccess: \u001b[0m\t14\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if no active session\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if read-tree fails\t\n\u001b[32mSuccess\u001b[0m\t||\tsnapshot.restore notifies error if checkout-index fails\t\n\t\n\u001b[32mSuccess: \u001b[0m\t4\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate ascending session IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate descending message IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate given IDs correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should throw error for invalid given IDs\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should validate schemas correctly\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should return available prefixes\t\n\u001b[32mSuccess\u001b[0m\t||\tID module should generate IDs with correct length structure\t\n\t\n\u001b[32mSuccess: \u001b[0m\t7\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new creates a new timer with required options\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer.new stores optional parameters\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a repeating timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start starts a one-shot timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start passes arguments to on_tick function\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick returns false\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops timer when on_tick throws an error\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start stops previous timer before starting new one\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:start throws error when timer creation fails\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop stops a running timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop calls on_stop callback when provided\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop does nothing when timer is not running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false when timer is not started\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns true when timer is running\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after timer is stopped\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Timer:is_running returns false after one-shot timer completes\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests can restart a stopped timer\t\n\u001b[32mSuccess\u001b[0m\t||\tTimer Integration tests handles rapid start/stop cycles\t\n\t\n\u001b[32mSuccess: \u001b[0m\t20\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\nTwo pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc\nTwo pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO\n\n========================================\t\nTesting: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays ansi-codes correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-abort correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays api-error correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays cursor_data correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays diff correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays markdown-codefence correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays mentions-with-ranges correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays perf correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-denied correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays permission correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays planning correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-all correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays redo-once correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays revert correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays selection correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays simple-session correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays tool-invalid correctly (session)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t\n\u001b[32mSuccess\u001b[0m\t||\trenderer replays updating-text correctly (session)\t\n\t\n\u001b[32mSuccess: \u001b[0m\t60\t\n\u001b[31mFailed : \u001b[0m\t0\t\n========================================\t\n\u001b[0;32m✓ Unit tests passed\u001b[0m\n------------------------------------------------\n\u001b[0;32mAll tests passed successfully!\u001b[0m\n" + }, + "type": "tool", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "reason": "tool-calls", + "cost": 0, + "id": "prt_a49edd837001lGPaOMUY99F0JS", + "messageID": "msg_a49edc4640017DZPFVUPHCm7Ji", + "snapshot": "4d76a8382f4cd173216ee6d5949f80aeec2ed395", + "type": "step-finish", + "tokens": { "reasoning": 0, "cache": { "read": 7680, "write": 0 }, "input": 15805, "output": 102 }, + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176910436 }, + "id": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 7680, "write": 0 }, "input": 15805, "output": 102 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176910436, "completed": 1762176915544 }, + "id": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 7680, "write": 0 }, "input": 15805, "output": 102 }, + "providerID": "github-copilot" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176910436, "completed": 1762176915546 }, + "id": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 7680, "write": 0 }, "input": 15805, "output": 102 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176910436, "completed": 1762176915547 }, + "id": "msg_a49edc4640017DZPFVUPHCm7Ji", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 7680, "write": 0 }, "input": 15805, "output": 102 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176915583 }, + "id": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 0, "write": 0 }, "input": 0, "output": 0 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "time": { "created": 1762176897494 }, + "summary": { + "diffs": [ + { + "file": "lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 1, + "additions": 5, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + } + ] + }, + "role": "user", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176915602 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Fixing param-type-not-match and need-check-nil in core.lua line 130", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_a49eddd62001iPYFPbJ6Y4rnHk", + "snapshot": "4d76a8382f4cd173216ee6d5949f80aeec2ed395", + "type": "step-start", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " of" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " for" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": "M" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " `" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ...,", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " ...," + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " the" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " now" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " line" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176916836 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + }, + "delta": " want" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "text": "All steps are complete and all tests pass! Here’s a summary of what was fixed:\n\n- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.\n- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.\n- ✅ Verified the fixes by running the full test suite—no errors or failures.\n\nYour file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!", + "id": "prt_a49eddd640016qrxxmOdhzVlPS", + "time": { "start": 1762176918065, "end": 1762176918065 }, + "type": "text", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "reason": "stop", + "cost": 0, + "id": "prt_a49ede231001JoNcJuwk65yXP6", + "messageID": "msg_a49edd87f001eOJDDoq5Vb5hra", + "snapshot": "4d76a8382f4cd173216ee6d5949f80aeec2ed395", + "type": "step-finish", + "tokens": { "reasoning": 0, "cache": { "read": 15744, "write": 0 }, "input": 24075, "output": 120 }, + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176915583 }, + "id": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15744, "write": 0 }, "input": 24075, "output": 120 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176915583, "completed": 1762176918091 }, + "id": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15744, "write": 0 }, "input": 24075, "output": 120 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176915583, "completed": 1762176918092 }, + "id": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15744, "write": 0 }, "input": 24075, "output": 120 }, + "providerID": "github-copilot" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "mode": "build", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_a49ed91d6001coTsjFq9x6FF5W", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Nov 03 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tcontext.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontext_bar.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\tprompt_guard_indicator.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\tthrottling_emitter.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tansi-codes.expected.json\n\t\tansi-codes.json\n\t\tapi-abort.expected.json\n\t\tapi-abort.json\n\t\tapi-error.expected.json\n\t\tapi-error.json\n\t\tcursor_data.expected.json\n\t\tcursor_data.json\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmarkdown-codefence.expected.json\n\t\tmarkdown-codefence.json\n\t\tmentions-with-ranges.expected.json\n\t\tmentions-with-ranges.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tperf.expected.json\n\t\tperf.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\tredo-all.expected.json\n\t\tredo-all.json\n\t\tredo-once.expected.json\n\t\tredo-once.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\treplay.ps1\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_bar_spec.lua\n\t\tcontext_completion_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 0\" \"+qa\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can also run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+qa\"`\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1762176915583, "completed": 1762176918093 }, + "id": "msg_a49edd87f001eOJDDoq5Vb5hra", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7", + "tokens": { "reasoning": 0, "cache": { "read": 15744, "write": 0 }, "input": 24075, "output": 120 }, + "providerID": "github-copilot" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_a49ed91d6001coTsjFq9x6FF5W", + "time": { "created": 1762176897494 }, + "summary": { + "diffs": [ + { + "file": "lua/opencode/core.lua", + "before": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { providerID = provider, modelID = model }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n", + "deletions": 1, + "additions": 5, + "after": "-- This file was written by an automated tool.\nlocal state = require('opencode.state')\nlocal context = require('opencode.context')\nlocal session = require('opencode.session')\nlocal ui = require('opencode.ui.ui')\nlocal server_job = require('opencode.server_job')\nlocal input_window = require('opencode.ui.input_window')\nlocal util = require('opencode.util')\nlocal config = require('opencode.config')\n\nlocal M = {}\nM._abort_count = 0\n\n---@param parent_id string?\nfunction M.select_session(parent_id)\n local all_sessions = session.get_all_workspace_sessions() or {}\n local filtered_sessions = vim.tbl_filter(function(s)\n return s.description ~= '' and s ~= nil and s.parentID == parent_id\n end, all_sessions)\n\n ui.select_session(filtered_sessions, function(selected_session)\n if not selected_session then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n state.active_session = selected_session\n if state.windows then\n state.restore_points = {}\n -- Don't need to update either renderer because they subscribe to\n -- session changes\n ui.focus_input()\n else\n M.open()\n end\n end)\nend\n\n---@param opts? OpenOpts\nfunction M.open(opts)\n opts = opts or { focus = 'input', new_session = false }\n\n if not state.opencode_server or not state.opencode_server:is_running() then\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n\n M.ensure_current_mode()\n\n local are_windows_closed = state.windows == nil\n\n if not require('opencode.ui.ui').is_opencode_focused() then\n require('opencode.context').load()\n end\n\n if are_windows_closed then\n -- Check if whether prompting will be allowed\n local context_module = require('opencode.context')\n local mentioned_files = context_module.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n if not allowed then\n vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN)\n end\n\n state.windows = ui.create_windows()\n end\n\n if opts.new_session then\n state.active_session = nil\n state.last_sent_context = nil\n state.active_session = M.create_new_session()\n else\n if not state.active_session then\n state.active_session = session.get_last_workspace_session()\n else\n if not state.display_route and are_windows_closed then\n -- We're not displaying /help or something like that but we have an active session\n -- and the windows were closed so we need to do a full refresh. This mostly happens\n -- when opening the window after having closed it since we're not currently clearing\n -- the session on api.close()\n ui.render_output(false)\n end\n end\n end\n\n if opts.focus == 'input' then\n ui.focus_input({ restore_position = are_windows_closed, start_insert = opts.start_insert == true })\n elseif opts.focus == 'output' then\n ui.focus_output({ restore_position = are_windows_closed })\n end\n state.is_opencode_focused = true\nend\n\n--- Sends a message to the active session, creating one if necessary.\n--- @param prompt string The message prompt to send.\n--- @param opts? SendMessageOpts\nfunction M.send_message(prompt, opts)\n local mentioned_files = context.context.mentioned_files or {}\n local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files)\n\n if not allowed then\n vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR)\n return\n end\n\n opts = opts or {}\n\n opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})\n state.current_context_config = opts.context\n context.load()\n opts.model = opts.model or state.current_model\n opts.agent = opts.agent or state.current_mode or config.default_mode\n\n local params = {}\n\n if opts.model then\n local provider, model = opts.model:match('^(.-)/(.+)$')\n params.model = { modelID = model, providerID = provider }\n state.current_model = opts.model\n end\n\n if opts.agent then\n params.agent = opts.agent\n state.current_mode = opts.agent\n end\n\n params.parts = context.format_message(prompt, opts.context)\n M.before_run(opts)\n\n if not state.active_session or not state.active_session.id then\n vim.notify('No active session to send message to', vim.log.levels.ERROR)\n return\n end\n state.api_client\n :create_message(state.active_session.id, params)\n :and_then(function(response)\n if not response or not response.info or not response.parts then\n -- fall back to full render. incremental render is handled\n -- event manager\n ui.render_output()\n end\n\n M.after_run(prompt)\n end)\n :catch(function(err)\n vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n M.cancel()\n end)\nend\n\n---@param title? string\n---@return Session?\nfunction M.create_new_session(title)\n local session_response = state.api_client\n :create_session(title and { title = title } or false)\n :catch(function(err)\n vim.notify('Error creating new session: ' .. vim.inspect(err), vim.log.levels.ERROR)\n end)\n :wait()\n\n if session_response and session_response.id then\n local new_session = session.get_by_id(session_response.id)\n return new_session\n end\nend\n\n---@param prompt string\nfunction M.after_run(prompt)\n context.unload_attachments()\n state.last_sent_context = vim.deepcopy(context.context)\n require('opencode.history').write(prompt)\n M._abort_count = 0\nend\n\n---@param opts? SendMessageOpts\nfunction M.before_run(opts)\n local is_new_session = opts and opts.new_session or not state.active_session\n opts = opts or {}\n\n M.cancel()\n -- ui.clear_output()\n\n M.open({\n new_session = is_new_session,\n })\nend\n\nfunction M.configure_provider()\n require('opencode.provider').select(function(selection)\n if not selection then\n if state.windows then\n ui.focus_input()\n end\n return\n end\n local model_str = string.format('%s/%s', selection.provider, selection.model)\n state.current_model = model_str\n\n if state.windows then\n require('opencode.ui.topbar').render()\n ui.focus_input()\n else\n vim.notify('Changed provider to ' .. selection.display, vim.log.levels.INFO)\n end\n end)\nend\n\nfunction M.cancel()\n if state.windows and state.active_session then\n if state.is_running() then\n M._abort_count = M._abort_count + 1\n\n -- if there's a current permission, reject it\n if state.current_permission then\n require('opencode.api').permission_deny()\n end\n\n local ok, result = pcall(function()\n return state.api_client:abort_session(state.active_session.id):wait()\n end)\n\n if not ok then\n vim.notify('Abort error: ' .. vim.inspect(result))\n end\n\n if M._abort_count >= 3 then\n vim.notify('Re-starting Opencode server')\n M._abort_count = 0\n -- close existing server\n if state.opencode_server then\n state.opencode_server:shutdown():wait()\n end\n\n -- start a new one\n state.opencode_server = nil\n\n -- NOTE: start a new server here to make sure we're subscribed\n -- to server events before a user sends a message\n state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]\n end\n end\n require('opencode.ui.footer').clear()\n input_window.set_content('')\n require('opencode.history').index = nil\n ui.focus_input()\n end\nend\n\nfunction M.opencode_ok()\n if vim.fn.executable('opencode') == 0 then\n vim.notify(\n 'opencode command not found - please install and configure opencode before using this plugin',\n vim.log.levels.ERROR\n )\n return false\n end\n\n if not state.opencode_cli_version or state.opencode_cli_version == '' then\n local result = vim.system({ 'opencode', '--version' }):wait()\n local out = (result and result.stdout or ''):gsub('%s+$', '')\n state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out\n end\n\n local required = state.required_version\n local current_version = state.opencode_cli_version\n\n if not current_version or current_version == '' then\n vim.notify(string.format('Unable to detect opencode CLI version. Requires >= %s', required), vim.log.levels.ERROR)\n return false\n end\n\n if not util.is_version_greater_or_equal(current_version, required) then\n vim.notify(\n string.format('Unsupported opencode CLI version: %s. Requires >= %s', current_version, required),\n vim.log.levels.ERROR\n )\n return false\n end\n\n return true\nend\n\nlocal function on_opencode_server()\n state.current_permission = nil\nend\n\n--- Switches the current mode to the specified agent.\n--- @param mode string The agent/mode to switch to\n--- @return boolean success Returns true if the mode was switched successfully, false otherwise\nfunction M.switch_to_mode(mode)\n if not mode or mode == '' then\n vim.notify('Mode cannot be empty', vim.log.levels.ERROR)\n return false\n end\n\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not vim.tbl_contains(available_agents, mode) then\n vim.notify(\n string.format('Invalid mode \"%s\". Available modes: %s', mode, table.concat(available_agents, ', ')),\n vim.log.levels.ERROR\n )\n return false\n end\n\n state.current_mode = mode\n ui.render_output()\n return true\nend\n\n--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.\n--- @return boolean success Returns true if current_mode is set\nfunction M.ensure_current_mode()\n if state.current_mode == nil then\n local config_file = require('opencode.config_file')\n local available_agents = config_file.get_opencode_agents()\n\n if not available_agents or #available_agents == 0 then\n vim.notify('No available agents found', vim.log.levels.ERROR)\n return false\n end\n\n local default_mode = config.default_mode\n\n -- Try to use the configured default mode if it's available\n if default_mode and vim.tbl_contains(available_agents, default_mode) then\n state.current_mode = default_mode\n else\n -- Fallback to first available agent\n state.current_mode = available_agents[1]\n end\n end\n return true\nend\n\nfunction M.setup()\n state.subscribe('opencode_server', on_opencode_server)\n\n vim.schedule(function()\n M.opencode_ok()\n end)\n local OpencodeApiClient = require('opencode.api_client')\n state.api_client = OpencodeApiClient.create()\nend\n\nreturn M\n" + } + ] + }, + "role": "user", + "sessionID": "ses_5b612cfbefferUqNCDHOrQunq7" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5b612cfbefferUqNCDHOrQunq7", + "time": { "created": 1762176872514, "updated": 1762176918135 }, + "summary": { "diffs": [] }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Fixing param-type-not-match and need-check-nil in core.lua line 130", + "version": "0.15.30" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/diff.expected.json b/tests/data/diff.expected.json index 8282a460..547ab7dc 100644 --- a/tests/data/diff.expected.json +++ b/tests/data/diff.expected.json @@ -1 +1,341 @@ -{"extmarks":[[1,1,0,{"ns_id":3,"priority":10,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-12 06:42:56)","OpencodeHint"],[" [msg_9d7287269001C5gRusYfX7A1w1]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false}],[2,2,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[3,3,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[4,3,39,{"ns_id":3,"hl_group":"OpencodeMention","end_row":3,"end_col":53,"end_right_gravity":false,"priority":1000,"right_gravity":true,"hl_eol":false}],[5,4,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[6,5,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[7,8,0,{"ns_id":3,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-12 06:42:56)","OpencodeHint"],[" [msg_9d7287287001HVwpPaH7WkRVdN]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false}],[8,10,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[9,11,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[10,12,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[11,13,0,{"ns_id":3,"end_col":0,"virt_text_pos":"overlay","virt_text":[["-","OpencodeDiffDelete"]],"priority":5000,"hl_group":"OpencodeDiffDelete","end_row":14,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false}],[12,13,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[13,14,0,{"ns_id":3,"end_col":0,"virt_text_pos":"overlay","virt_text":[["+","OpencodeDiffAdd"]],"priority":5000,"hl_group":"OpencodeDiffAdd","end_row":15,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false}],[14,14,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[15,15,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[16,16,0,{"ns_id":3,"priority":4096,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true}],[17,21,0,{"ns_id":3,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-12 06:43:03)","OpencodeHint"],[" [msg_9d7288f2f001hW6NqqhtBc72UU]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false}]],"timestamp":1769172144,"actions":[{"key":"R","args":["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"],"display_line":18,"range":{"from":18,"to":18},"type":"diff_revert_selected_file","text":"[R]evert file"},{"key":"A","args":["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"],"display_line":18,"range":{"from":18,"to":18},"type":"diff_revert_all","text":"Revert [A]ll"},{"key":"D","args":["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"],"display_line":18,"range":{"from":18,"to":18},"type":"diff_open","text":"[D]iff"}],"lines":["----","","","can you add \"great\" before \"string\" in @diff-test.txt?","","[`diff-test.txt`](diff-test.txt)","","----","","","** edit** `/Users/cam/tmp/a/diff-test.txt`","","`````txt"," this is a string"," this is a great string","","`````","","**󰻛 Created Snapshot** `1f593f7e`","","----","","",""]} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-12 06:42:56)", "OpencodeHint"], + [" [msg_9d7287269001C5gRusYfX7A1w1]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 3, + 39, + { + "ns_id": 3, + "hl_group": "OpencodeMention", + "end_row": 3, + "end_col": 53, + "end_right_gravity": false, + "priority": 1000, + "right_gravity": true, + "hl_eol": false + } + ], + [ + 5, + 4, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 5, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 8, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-12 06:42:56)", "OpencodeHint"], + [" [msg_9d7287287001HVwpPaH7WkRVdN]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 8, + 10, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 11, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 12, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 11, + 13, + 0, + { + "ns_id": 3, + "end_col": 0, + "virt_text_pos": "overlay", + "virt_text": [["-", "OpencodeDiffDelete"]], + "priority": 5000, + "hl_group": "OpencodeDiffDelete", + "end_row": 14, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 12, + 13, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 13, + 14, + 0, + { + "ns_id": 3, + "end_col": 0, + "virt_text_pos": "overlay", + "virt_text": [["+", "OpencodeDiffAdd"]], + "priority": 5000, + "hl_group": "OpencodeDiffAdd", + "end_row": 15, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 14, + 14, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 15, + 15, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 16, + 16, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 17, + 21, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-12 06:43:03)", "OpencodeHint"], + [" [msg_9d7288f2f001hW6NqqhtBc72UU]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ] + ], + "timestamp": 1769172144, + "actions": [ + { + "key": "R", + "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], + "display_line": 18, + "range": { "from": 18, "to": 18 }, + "type": "diff_revert_selected_file", + "text": "[R]evert file" + }, + { + "key": "A", + "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], + "display_line": 18, + "range": { "from": 18, "to": 18 }, + "type": "diff_revert_all", + "text": "Revert [A]ll" + }, + { + "key": "D", + "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], + "display_line": 18, + "range": { "from": 18, "to": 18 }, + "type": "diff_open", + "text": "[D]iff" + } + ], + "lines": [ + "----", + "", + "", + "can you add \"great\" before \"string\" in @diff-test.txt?", + "", + "[`diff-test.txt`](diff-test.txt)", + "", + "----", + "", + "", + "** edit** `/Users/cam/tmp/a/diff-test.txt`", + "", + "`````txt", + " this is a string", + " this is a great string", + "", + "`````", + "", + "**󰻛 Created Snapshot** `1f593f7e`", + "", + "----", + "", + "", + "" + ] +} diff --git a/tests/data/markdown-codefence.expected.json b/tests/data/markdown-codefence.expected.json index 84859402..755db80c 100644 --- a/tests/data/markdown-codefence.expected.json +++ b/tests/data/markdown-codefence.expected.json @@ -1 +1,590 @@ -{"lines":["----","","","Now let me also update the Run opts examples to use the new command syntax:","","** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/README.md`","","`````markdown"," "," Run a prompt in a new session using the Plan agent and disabling current file context:"," "," ```vim"," :OpencodeRunNewSession \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false"," :OpencodeRun \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4"," :Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false"," :Opencode run \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4"," ```"," "," ##👮 Permissions"," ","","`````","","**󰻛 Created Snapshot** `c64ddee8`","","----","","","README updated with the new nested command structure. The documentation now shows the new `:Opencode` subcommand syntax and includes a note that legacy `Opencode*` commands are still available by default but will be removed in the future.","","----","","","can you rename Opencode stop to Opencode cancel (and rename the api call)","","[`README.md`](README.md)","","----","","","Here's the plan to rename `Opencode stop` to `Opencode cancel` and update the API call accordingly:","","---","","## 📝 Plan: Rename Opencode Stop to Cancel","","### 1. Update Command Table and Documentation","- In  README.md, change:"," - Command: `:Opencode stop` → `:Opencode cancel`"," - API: `require('opencode.api').stop()` → `require('opencode.api').cancel()`"," - Keymap: `` remains the same (unless you want to change it)","","### 2. Update Codebase","- In  `lua/opencode/api.lua`:"," - Rename the function `M.stop()` to `M.cancel()`"," - Update all references to `stop` (command registration, legacy command map, subcommand routing, etc.) to use `cancel`"," - Ensure legacy command `OpencodeStop` still works (with deprecation warning), but routes to `cancel`","- In any other files (keymap config, tests, etc.) update references to `stop` to `cancel` as needed","","### 3. Update Tests","- In  `tests/unit/api_spec.lua`:"," - Update any tests that check for `stop` to check for `cancel`","","### 4. Update Slash Commands (if applicable)","- If there is a `/stop` slash command, rename it to `/cancel`","","### 5. Verify","- Run all tests to ensure everything works","- Check that both `:Opencode cancel` and legacy `:OpencodeStop` work (with warning)","","---","","**Note:** You are currently in \"Plan mode\" (read-only phase), so I cannot make edits right now. ","When you switch to edit mode, I will execute this plan step by step and verify all changes.","","Would you like to proceed with this plan when edits are allowed?","",""],"extmarks":[[1,1,0,{"ns_id":3,"virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-28 21:53:58)","OpencodeHint"],[" [msg_a2cd04588001P9plKmrFnsNH3M]","OpencodeHint"]],"right_gravity":true}],[2,5,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[3,6,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[4,7,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[5,8,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[6,9,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[7,10,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[8,11,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[9,12,0,{"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":5000,"virt_text":[["-","OpencodeDiffDelete"]],"ns_id":3,"hl_group":"OpencodeDiffDelete","right_gravity":true,"end_col":0,"end_row":13,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false}],[10,12,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[11,13,0,{"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":5000,"virt_text":[["-","OpencodeDiffDelete"]],"ns_id":3,"hl_group":"OpencodeDiffDelete","right_gravity":true,"end_col":0,"end_row":14,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false}],[12,13,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[13,14,0,{"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":5000,"virt_text":[["+","OpencodeDiffAdd"]],"ns_id":3,"hl_group":"OpencodeDiffAdd","right_gravity":true,"end_col":0,"end_row":15,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false}],[14,14,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[15,15,0,{"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":5000,"virt_text":[["+","OpencodeDiffAdd"]],"ns_id":3,"hl_group":"OpencodeDiffAdd","right_gravity":true,"end_col":0,"end_row":16,"hl_eol":true,"end_right_gravity":false,"virt_text_hide":false}],[16,15,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[17,16,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[18,17,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[19,18,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[20,19,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[21,20,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[22,21,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-1,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true}],[23,26,0,{"ns_id":3,"virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-28 21:54:05)","OpencodeHint"],[" [msg_a2cd062fb001UA0ZzR6JxgLxDQ]","OpencodeHint"]],"right_gravity":true}],[24,31,0,{"ns_id":3,"virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-28 22:34:58)","OpencodeHint"],[" [msg_a2cf5ce65001YLvVsYxIboFcP4]","OpencodeHint"]],"right_gravity":true}],[25,32,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true}],[26,33,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true}],[27,34,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true}],[28,35,0,{"ns_id":3,"virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true}],[29,38,0,{"ns_id":3,"virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-28 22:34:58)","OpencodeHint"],[" [msg_a2cf5cf0f002oBAjmnGIMGXjez]","OpencodeHint"]],"right_gravity":true}]],"actions":[{"text":"[R]evert file","args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_revert_selected_file","key":"R","display_line":23,"range":{"to":23,"from":23}},{"text":"Revert [A]ll","args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_revert_all","key":"A","display_line":23,"range":{"to":23,"from":23}},{"text":"[D]iff","args":["c64ddee834f1b802020a8f880eafa689f0b99406"],"type":"diff_open","key":"D","display_line":23,"range":{"to":23,"from":23}}],"timestamp":1769172150} +{ + "lines": [ + "----", + "", + "", + "Now let me also update the Run opts examples to use the new command syntax:", + "", + "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/README.md`", + "", + "`````markdown", + " ", + " Run a prompt in a new session using the Plan agent and disabling current file context:", + " ", + " ```vim", + " :OpencodeRunNewSession \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", + " :OpencodeRun \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", + " :Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", + " :Opencode run \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", + " ```", + " ", + " ##👮 Permissions", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `c64ddee8`", + "", + "----", + "", + "", + "README updated with the new nested command structure. The documentation now shows the new `:Opencode` subcommand syntax and includes a note that legacy `Opencode*` commands are still available by default but will be removed in the future.", + "", + "----", + "", + "", + "can you rename Opencode stop to Opencode cancel (and rename the api call)", + "", + "[`README.md`](README.md)", + "", + "----", + "", + "", + "Here's the plan to rename `Opencode stop` to `Opencode cancel` and update the API call accordingly:", + "", + "---", + "", + "## 📝 Plan: Rename Opencode Stop to Cancel", + "", + "### 1. Update Command Table and Documentation", + "- In  README.md, change:", + " - Command: `:Opencode stop` → `:Opencode cancel`", + " - API: `require('opencode.api').stop()` → `require('opencode.api').cancel()`", + " - Keymap: `` remains the same (unless you want to change it)", + "", + "### 2. Update Codebase", + "- In  `lua/opencode/api.lua`:", + " - Rename the function `M.stop()` to `M.cancel()`", + " - Update all references to `stop` (command registration, legacy command map, subcommand routing, etc.) to use `cancel`", + " - Ensure legacy command `OpencodeStop` still works (with deprecation warning), but routes to `cancel`", + "- In any other files (keymap config, tests, etc.) update references to `stop` to `cancel` as needed", + "", + "### 3. Update Tests", + "- In  `tests/unit/api_spec.lua`:", + " - Update any tests that check for `stop` to check for `cancel`", + "", + "### 4. Update Slash Commands (if applicable)", + "- If there is a `/stop` slash command, rename it to `/cancel`", + "", + "### 5. Verify", + "- Run all tests to ensure everything works", + "- Check that both `:Opencode cancel` and legacy `:OpencodeStop` work (with warning)", + "", + "---", + "", + "**Note:** You are currently in \"Plan mode\" (read-only phase), so I cannot make edits right now. ", + "When you switch to edit mode, I will execute this plan step by step and verify all changes.", + "", + "Would you like to proceed with this plan when edits are allowed?", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-28 21:53:58)", "OpencodeHint"], + [" [msg_a2cd04588001P9plKmrFnsNH3M]", "OpencodeHint"] + ], + "right_gravity": true + } + ], + [ + 2, + 5, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 3, + 6, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 4, + 7, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 5, + 8, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 6, + 9, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 7, + 10, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 8, + 11, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 9, + 12, + 0, + { + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 5000, + "virt_text": [["-", "OpencodeDiffDelete"]], + "ns_id": 3, + "hl_group": "OpencodeDiffDelete", + "right_gravity": true, + "end_col": 0, + "end_row": 13, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false + } + ], + [ + 10, + 12, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 11, + 13, + 0, + { + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 5000, + "virt_text": [["-", "OpencodeDiffDelete"]], + "ns_id": 3, + "hl_group": "OpencodeDiffDelete", + "right_gravity": true, + "end_col": 0, + "end_row": 14, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false + } + ], + [ + 12, + 13, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 13, + 14, + 0, + { + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 5000, + "virt_text": [["+", "OpencodeDiffAdd"]], + "ns_id": 3, + "hl_group": "OpencodeDiffAdd", + "right_gravity": true, + "end_col": 0, + "end_row": 15, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false + } + ], + [ + 14, + 14, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 15, + 15, + 0, + { + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 5000, + "virt_text": [["+", "OpencodeDiffAdd"]], + "ns_id": 3, + "hl_group": "OpencodeDiffAdd", + "right_gravity": true, + "end_col": 0, + "end_row": 16, + "hl_eol": true, + "end_right_gravity": false, + "virt_text_hide": false + } + ], + [ + 16, + 15, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 17, + 16, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 18, + 17, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 19, + 18, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 20, + 19, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 21, + 20, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 22, + 21, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -1, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true + } + ], + [ + 23, + 26, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-28 21:54:05)", "OpencodeHint"], + [" [msg_a2cd062fb001UA0ZzR6JxgLxDQ]", "OpencodeHint"] + ], + "right_gravity": true + } + ], + [ + 24, + 31, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-28 22:34:58)", "OpencodeHint"], + [" [msg_a2cf5ce65001YLvVsYxIboFcP4]", "OpencodeHint"] + ], + "right_gravity": true + } + ], + [ + 25, + 32, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true + } + ], + [ + 26, + 33, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true + } + ], + [ + 27, + 34, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true + } + ], + [ + 28, + 35, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true + } + ], + [ + 29, + 38, + 0, + { + "ns_id": 3, + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-10-28 22:34:58)", "OpencodeHint"], + [" [msg_a2cf5cf0f002oBAjmnGIMGXjez]", "OpencodeHint"] + ], + "right_gravity": true + } + ] + ], + "actions": [ + { + "text": "[R]evert file", + "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], + "type": "diff_revert_selected_file", + "key": "R", + "display_line": 23, + "range": { "to": 23, "from": 23 } + }, + { + "text": "Revert [A]ll", + "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], + "type": "diff_revert_all", + "key": "A", + "display_line": 23, + "range": { "to": 23, "from": 23 } + }, + { + "text": "[D]iff", + "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], + "type": "diff_open", + "key": "D", + "display_line": 23, + "range": { "to": 23, "from": 23 } + } + ], + "timestamp": 1769172150 +} diff --git a/tests/data/mentions-with-ranges.expected.json b/tests/data/mentions-with-ranges.expected.json index b50c4229..789362b0 100644 --- a/tests/data/mentions-with-ranges.expected.json +++ b/tests/data/mentions-with-ranges.expected.json @@ -1 +1,384 @@ -{"actions":[],"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-12 23:38:21)","OpencodeHint"],[" [msg_9daca16bf0017x95VD45mw3k8Q]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[4,3,5,{"right_gravity":true,"end_right_gravity":false,"ns_id":3,"priority":1000,"end_col":44,"end_row":3,"hl_eol":false,"hl_group":"OpencodeMention"}],[5,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[6,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[7,5,23,{"right_gravity":true,"end_right_gravity":false,"ns_id":3,"priority":1000,"end_col":48,"end_row":5,"hl_eol":false,"hl_group":"OpencodeMention"}],[8,6,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[9,7,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[10,8,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[11,9,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[12,10,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[13,11,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[14,12,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[15,13,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[16,14,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[17,15,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[18,16,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[19,17,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[20,18,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[21,19,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[22,20,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}],[23,21,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"ns_id":3,"virt_text_win_col":-3,"virt_text_hide":false,"right_gravity":true}]],"timestamp":1766432124,"lines":["----","","","when @lua/opencode/ui/streaming_renderer.lua renders a diff, only the first character, the + has the extmark. the rest of the line doesn't seem to have the highlight?","","here's an example from @tests/data/planning.json","","** edit** `diff-test.txt`","","```txt","-this is a string","+this is a great string","","```","","the - and the + are highlighted in the right color but none of the text is. any ideas?","","[`lua/opencode/ui/streaming_renderer.lua`](lua/opencode/ui/streaming_renderer.lua)","","[`tests/data/planning.json`](tests/data/planning.json)","","[`lua/opencode/ui/session_formatter.lua`](lua/opencode/ui/session_formatter.lua)","",""]} +{ + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-12 23:38:21)", "OpencodeHint"], + [" [msg_9daca16bf0017x95VD45mw3k8Q]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 4, + 3, + 5, + { + "right_gravity": true, + "end_right_gravity": false, + "ns_id": 3, + "priority": 1000, + "end_col": 44, + "end_row": 3, + "hl_eol": false, + "hl_group": "OpencodeMention" + } + ], + [ + 5, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 6, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 7, + 5, + 23, + { + "right_gravity": true, + "end_right_gravity": false, + "ns_id": 3, + "priority": 1000, + "end_col": 48, + "end_row": 5, + "hl_eol": false, + "hl_group": "OpencodeMention" + } + ], + [ + 8, + 6, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 9, + 7, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 10, + 8, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 11, + 9, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 12, + 10, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 13, + 11, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 14, + 12, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 15, + 13, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 16, + 14, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 17, + 15, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 18, + 16, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 19, + 17, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 20, + 18, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 21, + 19, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 22, + 20, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ], + [ + 23, + 21, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "ns_id": 3, + "virt_text_win_col": -3, + "virt_text_hide": false, + "right_gravity": true + } + ] + ], + "timestamp": 1766432124, + "lines": [ + "----", + "", + "", + "when @lua/opencode/ui/streaming_renderer.lua renders a diff, only the first character, the + has the extmark. the rest of the line doesn't seem to have the highlight?", + "", + "here's an example from @tests/data/planning.json", + "", + "** edit** `diff-test.txt`", + "", + "```txt", + "-this is a string", + "+this is a great string", + "", + "```", + "", + "the - and the + are highlighted in the right color but none of the text is. any ideas?", + "", + "[`lua/opencode/ui/streaming_renderer.lua`](lua/opencode/ui/streaming_renderer.lua)", + "", + "[`tests/data/planning.json`](tests/data/planning.json)", + "", + "[`lua/opencode/ui/session_formatter.lua`](lua/opencode/ui/session_formatter.lua)", + "", + "" + ] +} diff --git a/tests/data/message-removal.expected.json b/tests/data/message-removal.expected.json index 0ec635ea..80c014ae 100644 --- a/tests/data/message-removal.expected.json +++ b/tests/data/message-removal.expected.json @@ -1 +1,222 @@ -{"timestamp":1766432124,"actions":[],"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-09 08:53:21)","OpencodeHint"],[" [msg_001]","OpencodeHint"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[4,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[5,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[6,6,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[7,7,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[8,8,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[9,9,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[10,12,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],["","OpencodeHint"],[" (2025-10-09 08:53:22)","OpencodeHint"],[" [msg_002]","OpencodeHint"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}],[11,21,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],["","OpencodeHint"],[" (2025-10-09 08:53:24)","OpencodeHint"],[" [msg_004]","OpencodeHint"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}]],"lines":["----","","","Message 1, Part 1","","Message 1, Part 2","","Message 1, Part 4","","Message 1, Part 5","","----","","","Message 2, Part 2","","Message 2, Part 3","","Message 2, Part 4","","----","","","Message 4, Part 1","","Message 4, Part 5","",""]} \ No newline at end of file +{ + "timestamp": 1766432124, + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-09 08:53:21)", "OpencodeHint"], + [" [msg_001]", "OpencodeHint"] + ], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 6, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 7, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 8, + 8, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 9, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 12, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + ["", "OpencodeHint"], + [" (2025-10-09 08:53:22)", "OpencodeHint"], + [" [msg_002]", "OpencodeHint"] + ], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ], + [ + 11, + 21, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + ["", "OpencodeHint"], + [" (2025-10-09 08:53:24)", "OpencodeHint"], + [" [msg_004]", "OpencodeHint"] + ], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ] + ], + "lines": [ + "----", + "", + "", + "Message 1, Part 1", + "", + "Message 1, Part 2", + "", + "Message 1, Part 4", + "", + "Message 1, Part 5", + "", + "----", + "", + "", + "Message 2, Part 2", + "", + "Message 2, Part 3", + "", + "Message 2, Part 4", + "", + "----", + "", + "", + "Message 4, Part 1", + "", + "Message 4, Part 5", + "", + "" + ] +} diff --git a/tests/data/multiple-messages-synthetic.expected.json b/tests/data/multiple-messages-synthetic.expected.json index bc2e1e89..939536d7 100644 --- a/tests/data/multiple-messages-synthetic.expected.json +++ b/tests/data/multiple-messages-synthetic.expected.json @@ -1 +1,277 @@ -{"timestamp":1766432125,"lines":["----","","","Message 1","","----","","","Response 2 - Part 1","","Response 2 - Part 2","","Response 2 - Part 3","","Response 2 - Part 4 (late arrival)","","----","","","Message 3","","----","","","Response 4 - Part 1","","Response 4 - Part 2","","Response 4 - Part 3","","Response 4 - Part 4 (late arrival)","","----","","","Message 5","","----","","","Response 6 - Part 1","","Response 6 - Part 2","","Response 6 - Part 3","",""],"extmarks":[[1,1,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2001-09-09 01:46:41)","OpencodeHint"],[" [msg_001]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[2,2,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[3,3,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[4,6,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],["","OpencodeHint"],[" (2001-09-09 01:46:42)","OpencodeHint"],[" [msg_002]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[5,17,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2001-09-09 01:46:43)","OpencodeHint"],[" [msg_003]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[6,18,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[7,19,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[8,22,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],["","OpencodeHint"],[" (2001-09-09 01:46:44)","OpencodeHint"],[" [msg_004]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[9,33,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2001-09-09 01:46:45)","OpencodeHint"],[" [msg_005]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}],[10,34,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[11,35,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096}],[12,38,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],["","OpencodeHint"],[" (2001-09-09 01:46:46)","OpencodeHint"],[" [msg_006]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10}]],"actions":[]} \ No newline at end of file +{ + "timestamp": 1766432125, + "lines": [ + "----", + "", + "", + "Message 1", + "", + "----", + "", + "", + "Response 2 - Part 1", + "", + "Response 2 - Part 2", + "", + "Response 2 - Part 3", + "", + "Response 2 - Part 4 (late arrival)", + "", + "----", + "", + "", + "Message 3", + "", + "----", + "", + "", + "Response 4 - Part 1", + "", + "Response 4 - Part 2", + "", + "Response 4 - Part 3", + "", + "Response 4 - Part 4 (late arrival)", + "", + "----", + "", + "", + "Message 5", + "", + "----", + "", + "", + "Response 6 - Part 1", + "", + "Response 6 - Part 2", + "", + "Response 6 - Part 3", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:41)", "OpencodeHint"], + [" [msg_001]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 2, + 2, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 3, + 3, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 4, + 6, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:42)", "OpencodeHint"], + [" [msg_002]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 5, + 17, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:43)", "OpencodeHint"], + [" [msg_003]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 6, + 18, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 7, + 19, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 8, + 22, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:44)", "OpencodeHint"], + [" [msg_004]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 9, + 33, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:45)", "OpencodeHint"], + [" [msg_005]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ], + [ + 10, + 34, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 11, + 35, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096 + } + ], + [ + 12, + 38, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + ["", "OpencodeHint"], + [" (2001-09-09 01:46:46)", "OpencodeHint"], + [" [msg_006]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10 + } + ] + ], + "actions": [] +} diff --git a/tests/data/multiple-messages-synthetic.json b/tests/data/multiple-messages-synthetic.json index 2552c4d8..322f1b8d 100644 --- a/tests/data/multiple-messages-synthetic.json +++ b/tests/data/multiple-messages-synthetic.json @@ -9,7 +9,7 @@ "info": { "id": "msg_001", "role": "user", - "time": {"created": 1000000001}, + "time": { "created": 1000000001 }, "sessionID": "ses_test" } } @@ -32,7 +32,7 @@ "info": { "id": "msg_002", "role": "assistant", - "time": {"created": 1000000002}, + "time": { "created": 1000000002 }, "sessionID": "ses_test" } } @@ -43,7 +43,7 @@ "info": { "id": "msg_003", "role": "user", - "time": {"created": 1000000003}, + "time": { "created": 1000000003 }, "sessionID": "ses_test" } } @@ -66,7 +66,7 @@ "info": { "id": "msg_004", "role": "assistant", - "time": {"created": 1000000004}, + "time": { "created": 1000000004 }, "sessionID": "ses_test" } } @@ -77,7 +77,7 @@ "info": { "id": "msg_005", "role": "user", - "time": {"created": 1000000005}, + "time": { "created": 1000000005 }, "sessionID": "ses_test" } } @@ -100,7 +100,7 @@ "info": { "id": "msg_006", "role": "assistant", - "time": {"created": 1000000006}, + "time": { "created": 1000000006 }, "sessionID": "ses_test" } } diff --git a/tests/data/multiple-messages.expected.json b/tests/data/multiple-messages.expected.json index 7f6f398c..59334242 100644 --- a/tests/data/multiple-messages.expected.json +++ b/tests/data/multiple-messages.expected.json @@ -1 +1,312 @@ -{"actions":[],"lines":["----","","","can you count to 10?","","[`a-empty.txt`](a-empty.txt)","","----","","","Sure! Here is a count from 1 to 10:","","1 ","2 ","3 ","4 ","5 ","6 ","7 ","8 ","9 ","10","","Let me know if you need anything else!","","----","","","can you say all of the letters?","","[`a-empty.txt`](a-empty.txt)","","----","","","Absolutely! Here are all the letters of the English alphabet:","","A ","B ","C ","D ","E ","F ","G ","H ","I ","J ","K ","L ","M ","N ","O ","P ","Q ","R ","S ","T ","U ","V ","W ","X ","Y ","Z","","If you need lowercase, here they are as well:","","a ","b ","c ","d ","e ","f ","g ","h ","i ","j ","k ","l ","m ","n ","o ","p ","q ","r ","s ","t ","u ","v ","w ","x ","y ","z","","Let me know if you need anything else!","",""],"extmarks":[[1,1,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-11-01 03:32:54)","OpencodeHint"],[" [msg_a3d79a71f001dkQYw23QnYYElB]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col"}],[2,2,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[3,3,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[4,4,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[5,5,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[6,8,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-01 03:32:54)","OpencodeHint"],[" [msg_a3d79a771002NY7n6a0vJlriLH]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col"}],[7,26,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-11-01 03:32:55)","OpencodeHint"],[" [msg_a3d79aa5b0014RYY4My2AeTokd]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col"}],[8,27,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[9,28,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[10,29,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[11,30,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col"}],[12,33,0,{"virt_text_hide":false,"right_gravity":true,"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-11-01 03:32:56)","OpencodeHint"],[" [msg_a3d79ae610029QFKwb1uvSa4Lf]","OpencodeHint"]],"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col"}]],"timestamp":1766432125} +{ + "actions": [], + "lines": [ + "----", + "", + "", + "can you count to 10?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "Sure! Here is a count from 1 to 10:", + "", + "1 ", + "2 ", + "3 ", + "4 ", + "5 ", + "6 ", + "7 ", + "8 ", + "9 ", + "10", + "", + "Let me know if you need anything else!", + "", + "----", + "", + "", + "can you say all of the letters?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "Absolutely! Here are all the letters of the English alphabet:", + "", + "A ", + "B ", + "C ", + "D ", + "E ", + "F ", + "G ", + "H ", + "I ", + "J ", + "K ", + "L ", + "M ", + "N ", + "O ", + "P ", + "Q ", + "R ", + "S ", + "T ", + "U ", + "V ", + "W ", + "X ", + "Y ", + "Z", + "", + "If you need lowercase, here they are as well:", + "", + "a ", + "b ", + "c ", + "d ", + "e ", + "f ", + "g ", + "h ", + "i ", + "j ", + "k ", + "l ", + "m ", + "n ", + "o ", + "p ", + "q ", + "r ", + "s ", + "t ", + "u ", + "v ", + "w ", + "x ", + "y ", + "z", + "", + "Let me know if you need anything else!", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-11-01 03:32:54)", "OpencodeHint"], + [" [msg_a3d79a71f001dkQYw23QnYYElB]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col" + } + ], + [ + 2, + 2, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 3, + 3, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 4, + 4, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 5, + 5, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 6, + 8, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-01 03:32:54)", "OpencodeHint"], + [" [msg_a3d79a771002NY7n6a0vJlriLH]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col" + } + ], + [ + 7, + 26, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-11-01 03:32:55)", "OpencodeHint"], + [" [msg_a3d79aa5b0014RYY4My2AeTokd]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col" + } + ], + [ + 8, + 27, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 9, + 28, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 10, + 29, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 11, + 30, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col" + } + ], + [ + 12, + 33, + 0, + { + "virt_text_hide": false, + "right_gravity": true, + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-11-01 03:32:56)", "OpencodeHint"], + [" [msg_a3d79ae610029QFKwb1uvSa4Lf]", "OpencodeHint"] + ], + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col" + } + ] + ], + "timestamp": 1766432125 +} diff --git a/tests/data/multiple-messages.json b/tests/data/multiple-messages.json index 66430cb6..55282fe7 100644 --- a/tests/data/multiple-messages.json +++ b/tests/data/multiple-messages.json @@ -1 +1,576 @@ -[{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"id":"msg_a3d79a71f001dkQYw23QnYYElB","role":"user","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","time":{"created":1761967974176}}},"type":"message.updated"},{"properties":{"part":{"messageID":"msg_a3d79a71f001dkQYw23QnYYElB","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79a720001AuOiKCmZNuFlfx","text":"can you count to 10?","type":"text"}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79a71f001dkQYw23QnYYElB","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79a721001vPiC3ORNFSPNLN","type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/Users/cam/tmp/a/a-empty.txt\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79a71f001dkQYw23QnYYElB","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79a7210024gcCCV4inEDGE2","type":"text","text":"\n00001| \n00002| \n","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79a71f001dkQYw23QnYYElB","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","mime":"text/plain","id":"prt_a3d79a721003mwQTZPlnReIwnz","type":"file","filename":"a-empty.txt","url":"file:///Users/cam/tmp/a/a-empty.txt"}},"type":"message.part.updated"},{"properties":{"info":{"time":{"created":1761694675340,"updated":1761967974180},"title":"Reading /Users/cam/tmp/a/a-empty.txt file","id":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","version":"0.15.13","projectID":"b0b749d27ca2e03482d36bfe846b01ce40ba759b","directory":"/Users/cam/tmp/a"}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":0,"output":0,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"created":1761967974257},"providerID":"github-copilot","id":"msg_a3d79a771002NY7n6a0vJlriLH","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"id":"msg_a3d79aa5b0014RYY4My2AeTokd","role":"user","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","time":{"created":1761967975003}}},"type":"message.updated"},{"properties":{"part":{"messageID":"msg_a3d79aa5b0014RYY4My2AeTokd","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79aa5b002Zc9mag10BwPSv9","text":"can you say all of the letters?","type":"text"}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79aa5b0014RYY4My2AeTokd","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79aa5c0012pyT74SWYnjYkH","type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/Users/cam/tmp/a/a-empty.txt\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79aa5b0014RYY4My2AeTokd","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79aa5c002CTAJUeT1FWWR5O","type":"text","text":"\n00001| \n00002| \n","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79aa5b0014RYY4My2AeTokd","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","mime":"text/plain","id":"prt_a3d79aa5c003g3VlyxZf02j603","type":"file","filename":"a-empty.txt","url":"file:///Users/cam/tmp/a/a-empty.txt"}},"type":"message.part.updated"},{"properties":{"info":{"time":{"created":1761694675340,"updated":1761967975006},"title":"Reading /Users/cam/tmp/a/a-empty.txt file","id":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","version":"0.15.13","projectID":"b0b749d27ca2e03482d36bfe846b01ce40ba759b","directory":"/Users/cam/tmp/a"}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79a771002NY7n6a0vJlriLH","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79adc2001SlbfZoGL5h367h","type":"step-start","snapshot":"bdc851f4aabe4717f49be738dc8648d9da3e616c"}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79a771002NY7n6a0vJlriLH","time":{"start":1761967975875},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79adc3001o0SNyhd1vdCcJo","text":"Sure! Here is a count from 1 to 10:\n\n1 \n2 \n3 \n4 \n5 \n","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79a771002NY7n6a0vJlriLH","time":{"start":1761967975973,"end":1761967975973},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79adc3001o0SNyhd1vdCcJo","text":"Sure! Here is a count from 1 to 10:\n\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10\n\nLet me know if you need anything else!","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79a771002NY7n6a0vJlriLH","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","type":"step-finish","id":"prt_a3d79ae26001CSZsBO1VpcyyD1","snapshot":"bdc851f4aabe4717f49be738dc8648d9da3e616c","tokens":{"input":10019,"output":44,"reasoning":0,"cache":{"write":0,"read":0}},"cost":0}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10019,"output":44,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"created":1761967974257},"providerID":"github-copilot","id":"msg_a3d79a771002NY7n6a0vJlriLH","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10019,"output":44,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967976027,"created":1761967974257},"providerID":"github-copilot","id":"msg_a3d79a771002NY7n6a0vJlriLH","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10019,"output":44,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967976027,"created":1761967974257},"providerID":"github-copilot","id":"msg_a3d79a771002NY7n6a0vJlriLH","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10019,"output":44,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967976027,"created":1761967974257},"providerID":"github-copilot","id":"msg_a3d79a771002NY7n6a0vJlriLH","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":0,"output":0,"reasoning":0,"cache":{"write":0,"read":0}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"created":1761967976033},"providerID":"github-copilot","id":"msg_a3d79ae610029QFKwb1uvSa4Lf","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2a9001Fa6ClltBn6OMRM","type":"step-start","snapshot":"bdc851f4aabe4717f49be738dc8648d9da3e616c"}},"type":"message.part.updated"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \n","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977130},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm \nn \no \np \nq \nr \ns \nt \nu \nv \nw \nx \ny \n","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","time":{"start":1761967977770,"end":1761967977770},"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","id":"prt_a3d79b2aa001bs1N86JqEVkYMn","text":"Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm \nn \no \np \nq \nr \ns \nt \nu \nv \nw \nx \ny \nz\n\nLet me know if you need anything else!","type":"text"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"messageID":"msg_a3d79ae610029QFKwb1uvSa4Lf","sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","type":"step-finish","id":"prt_a3d79b52b001lumKGXmq5pGQTe","snapshot":"bdc851f4aabe4717f49be738dc8648d9da3e616c","tokens":{"input":10114,"output":138,"reasoning":0,"cache":{"write":0,"read":9728}},"cost":0}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10114,"output":138,"reasoning":0,"cache":{"write":0,"read":9728}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"created":1761967976033},"providerID":"github-copilot","id":"msg_a3d79ae610029QFKwb1uvSa4Lf","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10114,"output":138,"reasoning":0,"cache":{"write":0,"read":9728}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967977842,"created":1761967976033},"providerID":"github-copilot","id":"msg_a3d79ae610029QFKwb1uvSa4Lf","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10114,"output":138,"reasoning":0,"cache":{"write":0,"read":9728}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967977843,"created":1761967976033},"providerID":"github-copilot","id":"msg_a3d79ae610029QFKwb1uvSa4Lf","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5d2d08e73ffeW3TfuQ4l0iThiG","tokens":{"input":10114,"output":138,"reasoning":0,"cache":{"write":0,"read":9728}},"mode":"plan","cost":0,"modelID":"gpt-4.1","time":{"completed":1761967977843,"created":1761967976033},"providerID":"github-copilot","id":"msg_a3d79ae610029QFKwb1uvSa4Lf","role":"assistant","system":["You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n"],"path":{"root":"/Users/cam/tmp/a","cwd":"/Users/cam/tmp/a"}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"}] +[ + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "id": "msg_a3d79a71f001dkQYw23QnYYElB", + "role": "user", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "time": { "created": 1761967974176 } + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a71f001dkQYw23QnYYElB", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79a720001AuOiKCmZNuFlfx", + "text": "can you count to 10?", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a71f001dkQYw23QnYYElB", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79a721001vPiC3ORNFSPNLN", + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/Users/cam/tmp/a/a-empty.txt\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a71f001dkQYw23QnYYElB", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79a7210024gcCCV4inEDGE2", + "type": "text", + "text": "\n00001| \n00002| \n", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a71f001dkQYw23QnYYElB", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "mime": "text/plain", + "id": "prt_a3d79a721003mwQTZPlnReIwnz", + "type": "file", + "filename": "a-empty.txt", + "url": "file:///Users/cam/tmp/a/a-empty.txt" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "time": { "created": 1761694675340, "updated": 1761967974180 }, + "title": "Reading /Users/cam/tmp/a/a-empty.txt file", + "id": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "version": "0.15.13", + "projectID": "b0b749d27ca2e03482d36bfe846b01ce40ba759b", + "directory": "/Users/cam/tmp/a" + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 0, "output": 0, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1761967974257 }, + "providerID": "github-copilot", + "id": "msg_a3d79a771002NY7n6a0vJlriLH", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "id": "msg_a3d79aa5b0014RYY4My2AeTokd", + "role": "user", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "time": { "created": 1761967975003 } + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79aa5b0014RYY4My2AeTokd", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79aa5b002Zc9mag10BwPSv9", + "text": "can you say all of the letters?", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79aa5b0014RYY4My2AeTokd", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79aa5c0012pyT74SWYnjYkH", + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/Users/cam/tmp/a/a-empty.txt\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79aa5b0014RYY4My2AeTokd", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79aa5c002CTAJUeT1FWWR5O", + "type": "text", + "text": "\n00001| \n00002| \n", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79aa5b0014RYY4My2AeTokd", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "mime": "text/plain", + "id": "prt_a3d79aa5c003g3VlyxZf02j603", + "type": "file", + "filename": "a-empty.txt", + "url": "file:///Users/cam/tmp/a/a-empty.txt" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "time": { "created": 1761694675340, "updated": 1761967975006 }, + "title": "Reading /Users/cam/tmp/a/a-empty.txt file", + "id": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "version": "0.15.13", + "projectID": "b0b749d27ca2e03482d36bfe846b01ce40ba759b", + "directory": "/Users/cam/tmp/a" + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a771002NY7n6a0vJlriLH", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79adc2001SlbfZoGL5h367h", + "type": "step-start", + "snapshot": "bdc851f4aabe4717f49be738dc8648d9da3e616c" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a771002NY7n6a0vJlriLH", + "time": { "start": 1761967975875 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79adc3001o0SNyhd1vdCcJo", + "text": "Sure! Here is a count from 1 to 10:\n\n1 \n2 \n3 \n4 \n5 \n", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a771002NY7n6a0vJlriLH", + "time": { "start": 1761967975973, "end": 1761967975973 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79adc3001o0SNyhd1vdCcJo", + "text": "Sure! Here is a count from 1 to 10:\n\n1 \n2 \n3 \n4 \n5 \n6 \n7 \n8 \n9 \n10\n\nLet me know if you need anything else!", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79a771002NY7n6a0vJlriLH", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "type": "step-finish", + "id": "prt_a3d79ae26001CSZsBO1VpcyyD1", + "snapshot": "bdc851f4aabe4717f49be738dc8648d9da3e616c", + "tokens": { "input": 10019, "output": 44, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0 + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10019, "output": 44, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1761967974257 }, + "providerID": "github-copilot", + "id": "msg_a3d79a771002NY7n6a0vJlriLH", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10019, "output": 44, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967976027, "created": 1761967974257 }, + "providerID": "github-copilot", + "id": "msg_a3d79a771002NY7n6a0vJlriLH", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10019, "output": 44, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967976027, "created": 1761967974257 }, + "providerID": "github-copilot", + "id": "msg_a3d79a771002NY7n6a0vJlriLH", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10019, "output": 44, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967976027, "created": 1761967974257 }, + "providerID": "github-copilot", + "id": "msg_a3d79a771002NY7n6a0vJlriLH", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 0, "output": 0, "reasoning": 0, "cache": { "write": 0, "read": 0 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1761967976033 }, + "providerID": "github-copilot", + "id": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2a9001Fa6ClltBn6OMRM", + "type": "step-start", + "snapshot": "bdc851f4aabe4717f49be738dc8648d9da3e616c" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \n", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977130 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm \nn \no \np \nq \nr \ns \nt \nu \nv \nw \nx \ny \n", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "time": { "start": 1761967977770, "end": 1761967977770 }, + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "id": "prt_a3d79b2aa001bs1N86JqEVkYMn", + "text": "Absolutely! Here are all the letters of the English alphabet:\n\nA \nB \nC \nD \nE \nF \nG \nH \nI \nJ \nK \nL \nM \nN \nO \nP \nQ \nR \nS \nT \nU \nV \nW \nX \nY \nZ\n\nIf you need lowercase, here they are as well:\n\na \nb \nc \nd \ne \nf \ng \nh \ni \nj \nk \nl \nm \nn \no \np \nq \nr \ns \nt \nu \nv \nw \nx \ny \nz\n\nLet me know if you need anything else!", + "type": "text" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "messageID": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "type": "step-finish", + "id": "prt_a3d79b52b001lumKGXmq5pGQTe", + "snapshot": "bdc851f4aabe4717f49be738dc8648d9da3e616c", + "tokens": { "input": 10114, "output": 138, "reasoning": 0, "cache": { "write": 0, "read": 9728 } }, + "cost": 0 + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10114, "output": 138, "reasoning": 0, "cache": { "write": 0, "read": 9728 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "created": 1761967976033 }, + "providerID": "github-copilot", + "id": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10114, "output": 138, "reasoning": 0, "cache": { "write": 0, "read": 9728 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967977842, "created": 1761967976033 }, + "providerID": "github-copilot", + "id": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10114, "output": 138, "reasoning": 0, "cache": { "write": 0, "read": 9728 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967977843, "created": 1761967976033 }, + "providerID": "github-copilot", + "id": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5d2d08e73ffeW3TfuQ4l0iThiG", + "tokens": { "input": 10114, "output": 138, "reasoning": 0, "cache": { "write": 0, "read": 9728 } }, + "mode": "plan", + "cost": 0, + "modelID": "gpt-4.1", + "time": { "completed": 1761967977843, "created": 1761967976033 }, + "providerID": "github-copilot", + "id": "msg_a3d79ae610029QFKwb1uvSa4Lf", + "role": "assistant", + "system": [ + "You are opencode, an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.\n\nYour thinking should be thorough and so it's fine if it's very long. However, avoid unnecessary repetition and verbosity. You should be concise, but thorough.\n\nYou MUST iterate and keep going until the problem is solved.\n\nYou have everything you need to resolve this problem. I want you to fully solve this autonomously before coming back to me.\n\nOnly terminate your turn when you are sure that the problem is solved and all items have been checked off. Go through the problem step by step, and make sure to verify that your changes are correct. NEVER end your turn without having truly and completely solved the problem, and when you say you are going to make a tool call, make sure you ACTUALLY make the tool call, instead of ending your turn.\n\nTHE PROBLEM CAN NOT BE SOLVED WITHOUT EXTENSIVE INTERNET RESEARCH.\n\nYou must use the webfetch tool to recursively gather all information from URL's provided to you by the user, as well as any links you find in the content of those pages.\n\nYour knowledge on everything is out of date because your training date is in the past. \n\nYou CANNOT successfully complete this task without using Google to verify your\nunderstanding of third party packages and dependencies is up to date. You must use the webfetch tool to search google for how to properly use libraries, packages, frameworks, dependencies, etc. every single time you install or implement one. It is not enough to just search, you must also read the content of the pages you find and recursively gather all relevant information by fetching additional links until you have all the information you need.\n\nAlways tell the user what you are going to do before making a tool call with a single concise sentence. This will help them understand what you are doing and why.\n\nIf the user request is \"resume\" or \"continue\" or \"try again\", check the previous conversation history to see what the next incomplete step in the todo list is. Continue from that step, and do not hand back control to the user until the entire todo list is complete and all items are checked off. Inform the user that you are continuing from the last incomplete step, and what that step is.\n\nTake your time and think through every step - remember to check your solution rigorously and watch out for boundary cases, especially with the changes you made. Use the sequential thinking tool if available. Your solution must be perfect. If not, continue working on it. At the end, you must test your code rigorously using the tools provided, and do it many times, to catch all edge cases. If it is not robust, iterate more and make it perfect. Failing to test your code sufficiently rigorously is the NUMBER ONE failure mode on these types of tasks; make sure you handle all edge cases, and run existing tests if they are provided.\n\nYou MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.\n\nYou MUST keep working until the problem is completely solved, and all items in the todo list are checked off. Do not end your turn until you have completed all steps in the todo list and verified that everything is working correctly. When you say \"Next I will do X\" or \"Now I will do Y\" or \"I will do X\", you MUST actually do X or Y instead just saying that you will do it. \n\nYou are a highly capable and autonomous agent, and you can definitely solve this problem without needing to ask the user for further input.\n\n# Workflow\n1. Fetch any URL's provided by the user using the `webfetch` tool.\n2. Understand the problem deeply. Carefully read the issue and think critically about what is required. Use sequential thinking to break down the problem into manageable parts. Consider the following:\n - What is the expected behavior?\n - What are the edge cases?\n - What are the potential pitfalls?\n - How does this fit into the larger context of the codebase?\n - What are the dependencies and interactions with other parts of the code?\n3. Investigate the codebase. Explore relevant files, search for key functions, and gather context.\n4. Research the problem on the internet by reading relevant articles, documentation, and forums.\n5. Develop a clear, step-by-step plan. Break down the fix into manageable, incremental steps. Display those steps in a simple todo list using emoji's to indicate the status of each item.\n6. Implement the fix incrementally. Make small, testable code changes.\n7. Debug as needed. Use debugging techniques to isolate and resolve issues.\n8. Test frequently. Run tests after each change to verify correctness.\n9. Iterate until the root cause is fixed and all tests pass.\n10. Reflect and validate comprehensively. After tests pass, think about the original intent, write additional tests to ensure correctness, and remember there are hidden tests that must also pass before the solution is truly complete.\n\nRefer to the detailed sections below for more information on each step.\n\n## 1. Fetch Provided URLs\n- If the user provides a URL, use the `webfetch` tool to retrieve the content of the provided URL.\n- After fetching, review the content returned by the webfetch tool.\n- If you find any additional URLs or links that are relevant, use the `webfetch` tool again to retrieve those links.\n- Recursively gather all relevant information by fetching additional links until you have all the information you need.\n\n## 2. Deeply Understand the Problem\nCarefully read the issue and think hard about a plan to solve it before coding.\n\n## 3. Codebase Investigation\n- Explore relevant files and directories.\n- Search for key functions, classes, or variables related to the issue.\n- Read and understand relevant code snippets.\n- Identify the root cause of the problem.\n- Validate and update your understanding continuously as you gather more context.\n\n## 4. Internet Research\n- Use the `webfetch` tool to search google by fetching the URL `https://www.google.com/search?q=your+search+query`.\n- After fetching, review the content returned by the fetch tool.\n- You MUST fetch the contents of the most relevant links to gather information. Do not rely on the summary that you find in the search results.\n- As you fetch each link, read the content thoroughly and fetch any additional links that you find withhin the content that are relevant to the problem.\n- Recursively gather all relevant information by fetching links until you have all the information you need.\n\n## 5. Develop a Detailed Plan \n- Outline a specific, simple, and verifiable sequence of steps to fix the problem.\n- Create a todo list in markdown format to track your progress.\n- Each time you complete a step, check it off using `[x]` syntax.\n- Each time you check off a step, display the updated todo list to the user.\n- Make sure that you ACTUALLY continue on to the next step after checkin off a step instead of ending your turn and asking the user what they want to do next.\n\n## 6. Making Code Changes\n- Before editing, always read the relevant file contents or section to ensure complete context.\n- Always read 2000 lines of code at a time to ensure you have enough context.\n- If a patch is not applied correctly, attempt to reapply it.\n- Make small, testable, incremental changes that logically follow from your investigation and plan.\n- Whenever you detect that a project requires an environment variable (such as an API key or secret), always check if a .env file exists in the project root. If it does not exist, automatically create a .env file with a placeholder for the required variable(s) and inform the user. Do this proactively, without waiting for the user to request it.\n\n## 7. Debugging\n- Make code changes only if you have high confidence they can solve the problem\n- When debugging, try to determine the root cause rather than addressing symptoms\n- Debug for as long as needed to identify the root cause and identify a fix\n- Use print statements, logs, or temporary code to inspect program state, including descriptive statements or error messages to understand what's happening\n- To test hypotheses, you can also add test statements or functions\n- Revisit your assumptions if unexpected behavior occurs.\n\n\n# Communication Guidelines\nAlways communicate clearly and concisely in a casual, friendly yet professional tone. \n\n\"Let me fetch the URL you provided to gather more information.\"\n\"Ok, I've got all of the information I need on the LIFX API and I know how to use it.\"\n\"Now, I will search the codebase for the function that handles the LIFX API requests.\"\n\"I need to update several files here - stand by\"\n\"OK! Now let's run the tests to make sure everything is working correctly.\"\n\"Whelp - I see we have some problems. Let's fix those up.\"\n\n\n- Respond with clear, direct answers. Use bullet points and code blocks for structure. - Avoid unnecessary explanations, repetition, and filler. \n- Always write code directly to the correct files.\n- Do not display code to the user unless they specifically ask for it.\n- Only elaborate when clarification is essential for accuracy or user understanding.\n\n# Memory\nYou have a memory that stores information about the user and their preferences. This memory is used to provide a more personalized experience. You can access and update this memory as needed. The memory is stored in a file called `.github/instructions/memory.instruction.md`. If the file is empty, you'll need to create it. \n\nWhen creating a new memory file, you MUST include the following front matter at the top of the file:\n```yaml\n---\napplyTo: '**'\n---\n```\n\nIf the user asks you to remember something or add something to your memory, you can do so by updating the memory file.\n\n# Reading Files and Folders\n\n**Always check if you have already read a file, folder, or workspace structure before reading it again.**\n\n- If you have already read the content and it has not changed, do NOT re-read it.\n- Only re-read files or folders if:\n - You suspect the content has changed since your last read.\n - You have made edits to the file or folder.\n - You encounter an error that suggests the context may be stale or incomplete.\n- Use your internal memory and previous context to avoid redundant reads.\n- This will save time, reduce unnecessary operations, and make your workflow more efficient.\n\n# Writing Prompts\nIf you are asked to write a prompt, you should always generate the prompt in markdown format.\n\nIf you are not writing the prompt in a file, you should always wrap the prompt in triple backticks so that it is formatted correctly and can be easily copied from the chat.\n\nRemember that todo lists must always be written in markdown format and must always be wrapped in triple backticks.\n\n# Git \nIf the user tells you to stage and commit, you may do so. \n\nYou are NEVER allowed to stage and commit files automatically.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /Users/cam/tmp/a\n Is directory a git repo: yes\n Platform: darwin\n Today's date: Fri Oct 31 2025\n\n\n blah/\n\ttest.txt\nlua/\n\texample-plugin/\n\t\tinit.lua\nnvim-plugin-template/\n\tlua/\n\t\tnvim-plugin-template/\n\t\t\tconfig.lua\n\t\t\tinit.lua\na\na-empty.txt\na-markdown.md\na.go\na.md\na.txt\na2.txt\napi-abort\napi-errors\nbars.txt\ndiff-new\ndiff-new.json\ndiff-test.txt\ndiff.json\ndiff2-clean.json\ndiff2.json\nerrors\nerrors.json\ngo.txt\nmentions\nminimal.lua\nmultiple-messages\nother-a.txt\nother-file.txt\npermission.json\nselection\ntest\ntest.json\ntest.sql\ntest.txt\ntest3.json\n" + ], + "path": { "root": "/Users/cam/tmp/a", "cwd": "/Users/cam/tmp/a" } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" } +] diff --git a/tests/data/multiple-question-ask-reply-all.expected.json b/tests/data/multiple-question-ask-reply-all.expected.json index 23ef537f..c39fbdb5 100644 --- a/tests/data/multiple-question-ask-reply-all.expected.json +++ b/tests/data/multiple-question-ask-reply-all.expected.json @@ -1 +1,390 @@ -{"lines":["----","","","[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)","","can you use the question tool and ask me a couple of questions","","----","","","** question** ","","**Q1:** Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","**A1:** busted","","**Q2:** How do you usually run your tests in this codebase?","**A2:** run_tests.sh (Recommended)","","**Q3:** multi-line question","The most important question of all:","Which team are you on?","**A3:** Coffee","","**󰻛 Created Snapshot** `be64b620`","","----","","","Thanks for your answers! You've indicated:","","- Preferred testing framework: **busted**","- Typical test running method: ** run_tests.sh (Recommended)**","","I have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!","",""],"actions":[{"display_line":23,"range":{"from":23,"to":23},"type":"diff_revert_selected_file","args":["be64b6205f0da4c9240df2476dc35495429161b5"],"text":"[R]evert file","key":"R"},{"display_line":23,"range":{"from":23,"to":23},"type":"diff_revert_all","args":["be64b6205f0da4c9240df2476dc35495429161b5"],"text":"Revert [A]ll","key":"A"},{"display_line":23,"range":{"from":23,"to":23},"type":"diff_open","args":["be64b6205f0da4c9240df2476dc35495429161b5"],"text":"[D]iff","key":"D"}],"extmarks":[[1,1,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-26 14:30:46)","OpencodeHint"],[" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}],[2,2,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[3,3,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[4,4,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[5,5,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[6,6,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true}],[7,8,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:30:46)","OpencodeHint"],[" [msg_bfab6dc80001FueCN7E2691J2R]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}],[8,10,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[9,11,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[10,12,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[11,13,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[12,14,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[13,15,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[14,16,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[15,17,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[16,18,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[17,19,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[18,20,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[19,21,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"virt_text_repeat_linebreak":true}],[20,26,0,{"ns_id":3,"virt_text_hide":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:31:55)","OpencodeHint"],[" [msg_bfab7e7fd0018kA9yLHMkJM3fA]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false}]],"timestamp":1770681185} \ No newline at end of file +{ + "lines": [ + "----", + "", + "", + "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", + "", + "can you use the question tool and ask me a couple of questions", + "", + "----", + "", + "", + "** question** ", + "", + "**Q1:** Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "**A1:** busted", + "", + "**Q2:** How do you usually run your tests in this codebase?", + "**A2:** run_tests.sh (Recommended)", + "", + "**Q3:** multi-line question", + "The most important question of all:", + "Which team are you on?", + "**A3:** Coffee", + "", + "**󰻛 Created Snapshot** `be64b620`", + "", + "----", + "", + "", + "Thanks for your answers! You've indicated:", + "", + "- Preferred testing framework: **busted**", + "- Typical test running method: ** run_tests.sh (Recommended)**", + "", + "I have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!", + "", + "" + ], + "actions": [ + { + "display_line": 23, + "range": { "from": 23, "to": 23 }, + "type": "diff_revert_selected_file", + "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "text": "[R]evert file", + "key": "R" + }, + { + "display_line": 23, + "range": { "from": 23, "to": 23 }, + "type": "diff_revert_all", + "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "text": "Revert [A]ll", + "key": "A" + }, + { + "display_line": 23, + "range": { "from": 23, "to": 23 }, + "type": "diff_open", + "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "text": "[D]iff", + "key": "D" + } + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-26 14:30:46)", "OpencodeHint"], + [" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 4, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 5, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 6, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 8, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:30:46)", "OpencodeHint"], + [" [msg_bfab6dc80001FueCN7E2691J2R]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ], + [ + 8, + 10, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 11, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 12, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 11, + 13, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 12, + 14, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 13, + 15, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 14, + 16, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 15, + 17, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 16, + 18, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 17, + 19, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 18, + 20, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 19, + 21, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_repeat_linebreak": true + } + ], + [ + 20, + 26, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:31:55)", "OpencodeHint"], + [" [msg_bfab7e7fd0018kA9yLHMkJM3fA]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false + } + ] + ], + "timestamp": 1770681185 +} diff --git a/tests/data/multiple-question-ask-reply-all.json b/tests/data/multiple-question-ask-reply-all.json index cae12cc3..d6bb9fc1 100644 --- a/tests/data/multiple-question-ask-reply-all.json +++ b/tests/data/multiple-question-ask-reply-all.json @@ -1 +1,989 @@ -[{"type":"session.updated","properties":{"info":{"slug":"happy-harbor","time":{"created":1769437845874,"updated":1769437845874},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","id":"ses_40549268dffehKpDd14uHIMQaT","version":"1.1.31","title":"New session - 2026-01-26T14:30:45.874Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf4002ydMd2JojY4Nda0","synthetic":true}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf80012C7P5ugGXl7Kbv","synthetic":true}}},{"type":"message.part.updated","properties":{"part":{"mime":"text/plain","type":"file","sessionID":"ses_40549268dffehKpDd14uHIMQaT","id":"prt_bfab6dbf9001Kebk9O9AoUc4yI","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","filename":"tests/replay/renderer_spec.lua","url":"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"can you use the question tool and ask me a couple of questions","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf4001xrYv3BzlgWGgkv"}}},{"type":"session.updated","properties":{"info":{"slug":"happy-harbor","time":{"created":1769437845874,"updated":1769437846547},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","id":"ses_40549268dffehKpDd14uHIMQaT","version":"1.1.31","title":"New session - 2026-01-26T14:30:45.874Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dc80001FueCN7E2691J2R","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","time":{"created":1769437846657},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2026-01-26T14:30:45.874Z","version":"1.1.31","summary":{"additions":0,"deletions":0,"files":0},"time":{"created":1769437845874,"updated":1769437846728}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"be64b6205f0da4c9240df2476dc35495429161b5","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dc80001FueCN7E2691J2R","id":"prt_bfab6e0c10013qo2ZThCt7FqLA"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab6e0c4001ou3kcu0UICsKQq","tool":"question","sessionID":"ses_40549268dffehKpDd14uHIMQaT","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj","messageID":"msg_bfab6dc80001FueCN7E2691J2R","type":"tool","state":{"raw":"","input":{},"status":"pending"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.asked","properties":{"questions":[{"question":"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","header":"Testing Framework?","options":[{"label":"busted","description":"Busted is the most common Lua testing framework."},{"label":"plenary.nvim","description":"Plenary.nvim provides unit test utilities for Neovim plugins."},{"label":"other","description":"Other or custom test framework"}]},{"question":"How do you usually run your tests in this codebase?","header":"Run Tests Approach","options":[{"label":"run_tests.sh (Recommended)","description":"Use run_tests.sh script provided in repo root."},{"label":"Direct test runner","description":"Use individual test commands (e.g., invoke Busted/Plenary directly)."},{"label":"Other","description":"Other custom scripts or workflow"}]},{"question":"The most important question of all:\nWhich team are you on?","header":"multi-line question","options":[{"label":"Coffee","description":"#teamcoffee"},{"label":"Tea","description":"#teamtea"}]}],"id":"que_bfab6ef4f0010bzEmrR38ndiIA","sessionID":"ses_40549268dffehKpDd14uHIMQaT","tool":{"messageID":"msg_bfab6dc80001FueCN7E2691J2R","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab6e0c4001ou3kcu0UICsKQq","tool":"question","sessionID":"ses_40549268dffehKpDd14uHIMQaT","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj","messageID":"msg_bfab6dc80001FueCN7E2691J2R","type":"tool","state":{"input":{"questions":[{"question":"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","header":"Testing Framework?","options":[{"label":"busted","description":"Busted is the most common Lua testing framework."},{"label":"plenary.nvim","description":"Plenary.nvim provides unit test utilities for Neovim plugins."},{"label":"other","description":"Other or custom test framework"}]},{"question":"How do you usually run your tests in this codebase?","header":"Run Tests Approach","options":[{"label":"run_tests.sh (Recommended)","description":"Use run_tests.sh script provided in repo root."},{"label":"Direct test runner","description":"Use individual test commands (e.g., invoke Busted/Plenary directly)."},{"label":"Other","description":"Other custom scripts or workflow"}]},{"question":"The most important question of all:\nWhich team are you on?","header":"multi-line question","options":[{"label":"Coffee","description":"#teamcoffee"},{"label":"Tea","description":"#teamtea"}]}]},"time":{"start":1769437851471},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"title":"Ask me two questions","diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Ask questions about renderer_spec.lua","version":"1.1.31","summary":{"additions":0,"deletions":0,"files":0},"time":{"created":1769437845874,"updated":1769437846728}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.replied","properties":{"requestID":"que_bfab6ef4f0010bzEmrR38ndiIA","sessionID":"ses_40549268dffehKpDd14uHIMQaT","answers":[["busted"],["run_tests.sh (Recommended)"],["Coffee"]]}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab6e0c4001ou3kcu0UICsKQq","tool":"question","sessionID":"ses_40549268dffehKpDd14uHIMQaT","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj","messageID":"msg_bfab6dc80001FueCN7E2691J2R","type":"tool","state":{"metadata":{"answers":[["busted"],["run_tests.sh (Recommended)"],["Coffee"]],"truncated":false},"output":"User has answered your questions: \"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\"=\"busted\", \"How do you usually run your tests in this codebase?\"=\"run_tests.sh (Recommended)\". You can now continue with the user's answers in mind.","time":{"start":1769437851471,"end":1769437915067},"input":{"questions":[{"question":"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","header":"Testing Framework?","options":[{"label":"busted","description":"Busted is the most common Lua testing framework."},{"label":"plenary.nvim","description":"Plenary.nvim provides unit test utilities for Neovim plugins."},{"label":"other","description":"Other or custom test framework"}]},{"question":"How do you usually run your tests in this codebase?","header":"Run Tests Approach","options":[{"label":"run_tests.sh (Recommended)","description":"Use run_tests.sh script provided in repo root."},{"label":"Direct test runner","description":"Use individual test commands (e.g., invoke Busted/Plenary directly)."},{"label":"Other","description":"Other custom scripts or workflow"}]},{"question":"The most important question of all:\nWhich team are you on?","header":"multi-line question","options":[{"label":"Coffee","description":"#teamcoffee"},{"label":"Tea","description":"#teamtea"}]}]},"title":"Asked 3 questions","status":"completed"}}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab7e7c1001GZ9QrtUEO7l0zh","type":"step-finish","snapshot":"bef5baca5e1828492c87ba3e7d702707c782f6ec","sessionID":"ses_40549268dffehKpDd14uHIMQaT","cost":0,"reason":"tool-calls","tokens":{"reasoning":0,"input":2789,"output":176,"cache":{"write":0,"read":9984}},"messageID":"msg_bfab6dc80001FueCN7E2691J2R"}}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dc80001FueCN7E2691J2R","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","agent":"build","time":{"created":1769437846657},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","tokens":{"reasoning":0,"input":2789,"output":176,"cache":{"write":0,"read":9984}},"finish":"tool-calls"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"patch","sessionID":"ses_40549268dffehKpDd14uHIMQaT","id":"prt_bfab7e7f30018PKHf93IEAI3AX","messageID":"msg_bfab6dc80001FueCN7E2691J2R","hash":"be64b6205f0da4c9240df2476dc35495429161b5","files":["/home/francis/Projects/_nvim/opencode.nvim/tests/data/multiple-question-ask.json"]}}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dc80001FueCN7E2691J2R","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","agent":"build","time":{"created":1769437846657,"completed":1769437915127},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","tokens":{"reasoning":0,"input":2789,"output":176,"cache":{"write":0,"read":9984}},"finish":"tool-calls"}}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab7e7fd0018kA9yLHMkJM3fA","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","time":{"created":1769437915134},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"title":"Ask me two questions","diffs":[{"before":"[DEBUG ERROR] git show be64b6205f0da4c9240df2476dc35495429161b5:tests/data/multiple-question-ask.json failed: fatal: path 'tests/data/multiple-question-ask.json' exists on disk, but not in 'be64b6205f0da4c9240df2476dc35495429161b5'\n","after":"[{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437845874},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\\\"}\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4002ydMd2JojY4Nda0\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"\\n00001| local state = require('opencode.state')\\n00002| local ui = require('opencode.ui.ui')\\n00003| local helpers = require('tests.helpers')\\n00004| local output_window = require('opencode.ui.output_window')\\n00005| local assert = require('luassert')\\n00006| local config = require('opencode.config')\\n00007| \\n00008| local function assert_output_matches(expected, actual, name)\\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\\n00010| \\n00011| assert.are.equal(\\n00012| #expected.lines,\\n00013| #actual.lines,\\n00014| string.format(\\n00015| 'Line count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00016| #expected.lines,\\n00017| #actual.lines,\\n00018| math.min(#expected.lines, #actual.lines) + 1,\\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\\n00021| )\\n00022| )\\n00023| \\n00024| for i = 1, #expected.lines do\\n00025| assert.are.equal(\\n00026| expected.lines[i],\\n00027| actual.lines[i],\\n00028| string.format(\\n00029| 'Line %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00030| i,\\n00031| vim.inspect(expected.lines[i]),\\n00032| vim.inspect(actual.lines[i])\\n00033| )\\n00034| )\\n00035| end\\n00036| \\n00037| assert.are.equal(\\n00038| #expected.extmarks,\\n00039| #normalized_extmarks,\\n00040| string.format(\\n00041| 'Extmark count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00042| #expected.extmarks,\\n00043| #normalized_extmarks,\\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\\n00047| )\\n00048| )\\n00049| \\n00050| for i = 1, #expected.extmarks do\\n00051| assert.are.same(\\n00052| expected.extmarks[i],\\n00053| normalized_extmarks[i],\\n00054| string.format(\\n00055| 'Extmark %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00056| i,\\n00057| vim.inspect(expected.extmarks[i]),\\n00058| vim.inspect(normalized_extmarks[i])\\n00059| )\\n00060| )\\n00061| end\\n00062| \\n00063| local expected_action_count = expected.actions and #expected.actions or 0\\n00064| local actual_action_count = actual.actions and #actual.actions or 0\\n00065| \\n00066| assert.are.equal(\\n00067| expected_action_count,\\n00068| actual_action_count,\\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\\n00070| )\\n00071| \\n00072| if expected.actions then\\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\\n00074| local function sort_actions(actions)\\n00075| local sorted = vim.deepcopy(actions)\\n00076| table.sort(sorted, function(a, b)\\n00077| return vim.inspect(a) < vim.inspect(b)\\n00078| end)\\n00079| return sorted\\n00080| end\\n00081| \\n00082| assert.same(\\n00083| sort_actions(expected.actions),\\n00084| sort_actions(actual.actions),\\n00085| string.format(\\n00086| 'Actions mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00087| vim.inspect(expected.actions),\\n00088| vim.inspect(actual.actions)\\n00089| )\\n00090| )\\n00091| end\\n00092| end\\n00093| \\n00094| describe('renderer unit tests', function()\\n00095| local event_subscriptions = {\\n00096| 'session.updated',\\n00097| 'session.compacted',\\n00098| 'session.error',\\n00099| 'message.updated',\\n00100| 'message.removed',\\n00101| 'message.part.updated',\\n00102| 'message.part.removed',\\n00103| 'permission.updated',\\n00104| 'permission.replied',\\n00105| 'question.replied',\\n00106| 'question.asked',\\n00107| 'file.edited',\\n00108| 'custom.restore_point.created',\\n00109| 'custom.emit_events.finished',\\n00110| }\\n00111| \\n00112| before_each(function()\\n00113| require('opencode.event_manager').setup()\\n00114| end)\\n00115| \\n00116| it('subsribes to events correctly', function()\\n00117| local renderer = require('opencode.ui.renderer')\\n00118| local event_manager = state.event_manager\\n00119| \\n00120| event_manager.events = {}\\n00121| \\n00122| renderer.setup_subscriptions()\\n00123| \\n00124| for _, event_name in ipairs(event_subscriptions) do\\n00125| assert.is_true(\\n00126| event_manager.events[event_name] ~= nil,\\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\\n00128| )\\n00129| end\\n00130| end)\\n00131| \\n00132| it('unsubsribes from events correctly', function()\\n00133| local renderer = require('opencode.ui.renderer')\\n00134| local event_manager = state.event_manager\\n00135| \\n00136| renderer.setup_subscriptions()\\n00137| \\n00138| renderer.setup_subscriptions(false)\\n00139| \\n00140| for _, event_name in ipairs(event_subscriptions) do\\n00141| assert.is_true(\\n00142| vim.tbl_isempty(event_manager.events[event_name]),\\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\\n00144| )\\n00145| end\\n00146| end)\\n00147| end)\\n00148| \\n00149| describe('renderer functional tests', function()\\n00150| config.debug.show_ids = true\\n00151| \\n00152| before_each(function()\\n00153| helpers.replay_setup()\\n00154| end)\\n00155| \\n00156| after_each(function()\\n00157| if state.windows then\\n00158| ui.close_windows(state.windows)\\n00159| end\\n00160| end)\\n00161| \\n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\\n00163| \\n00164| -- Don't do the full session test on these files, usually\\n00165| -- because they involve permission prompts\\n00166| local skip_full_session = {\\n00167| 'permission-prompt',\\n00168| 'permission-ask-new',\\n00169| 'question-ask',\\n00170| 'shifting-and-multiple-perms',\\n00171| 'message-removal',\\n00172| }\\n00173| \\n00174| for _, filepath in ipairs(json_files) do\\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\\n00176| \\n00177| if not name:match('%.expected$') then\\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\\n00179| \\n00180| if vim.fn.filereadable(expected_path) == 1 then\\n00181| for i = 1, 2 do\\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\\n00183| it(\\n00184| 'replays '\\n00185| .. name\\n00186| .. ' correctly (event-by-event, '\\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\\n00188| .. ')',\\n00189| function()\\n00190| local events = helpers.load_test_data(filepath)\\n00191| state.active_session = helpers.get_session_from_events(events)\\n00192| local expected = helpers.load_test_data(expected_path)\\n00193| \\n00194| helpers.replay_events(events)\\n00195| vim.wait(1000, function()\\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\\n00197| end)\\n00198| \\n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00200| assert_output_matches(expected, actual, name)\\n00201| end\\n00202| )\\n00203| end\\n00204| \\n00205| if not vim.tbl_contains(skip_full_session, name) then\\n00206| it('replays ' .. name .. ' correctly (session)', function()\\n00207| local renderer = require('opencode.ui.renderer')\\n00208| local events = helpers.load_test_data(filepath)\\n00209| state.active_session = helpers.get_session_from_events(events, true)\\n00210| local expected = helpers.load_test_data(expected_path)\\n00211| \\n00212| local session_data = helpers.load_session_from_events(events)\\n00213| renderer._render_full_session_data(session_data)\\n00214| \\n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00216| assert_output_matches(expected, actual, name)\\n00217| end)\\n00218| end\\n00219| end\\n00220| end\\n00221| end\\n00222| end)\\n00223| \\n\\n(End of file - total 223 lines)\\n\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf80012C7P5ugGXl7Kbv\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"mime\":\"text/plain\",\"type\":\"file\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"id\":\"prt_bfab6dbf9001Kebk9O9AoUc4yI\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"filename\":\"tests/replay/renderer_spec.lua\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"can you use the question tool and ask me a couple of questions\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4001xrYv3BzlgWGgkv\"}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437846547},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"modelID\":\"gpt-4.1\",\"providerID\":\"github-copilot\",\"cost\":0,\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"mode\":\"build\",\"time\":{\"created\":1769437846657},\"role\":\"assistant\",\"path\":{\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"tokens\":{\"reasoning\":0,\"input\":0,\"output\":0,\"cache\":{\"write\":0,\"read\":0}},\"parentID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"step-start\",\"snapshot\":\"be64b6205f0da4c9240df2476dc35495429161b5\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"id\":\"prt_bfab6e0c10013qo2ZThCt7FqLA\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"raw\":\"\",\"input\":{},\"status\":\"pending\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"question.asked\",\"properties\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}],\"id\":\"que_bfab6ef4f0010bzEmrR38ndiIA\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"tool\":{\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"input\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}]},\"time\":{\"start\":1769437851471},\"status\":\"running\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"title\":\"Ask me two questions\",\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"Ask questions about renderer_spec.lua\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]}]\n","deletions":0,"additions":1,"file":"tests/data/multiple-question-ask.json"}]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Ask questions about renderer_spec.lua","version":"1.1.31","summary":{"additions":1,"deletions":0,"files":1},"time":{"created":1769437845874,"updated":1769437915176}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"bef5baca5e1828492c87ba3e7d702707c782f6ec","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb3f001iDZa7Ffqv7nqd7"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" answers"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" framework"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n-","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"-"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"_tests"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"I"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" any"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" or"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" tasks"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neov","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"ov"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase.","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"."}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" have"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" or"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" as"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification,","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":","}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" debugging"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" let"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" tailor"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" preferences"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":" for"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437915970}},"delta":"!"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA","id":"prt_bfab7eb42001Vm8SgJBX4UFE0B","time":{"start":1769437917971,"end":1769437917971}}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab7f314001BD9Kv6fskeX4QN","type":"step-finish","snapshot":"bef5baca5e1828492c87ba3e7d702707c782f6ec","sessionID":"ses_40549268dffehKpDd14uHIMQaT","cost":0,"reason":"stop","tokens":{"reasoning":0,"input":88,"output":99,"cache":{"write":0,"read":12928}},"messageID":"msg_bfab7e7fd0018kA9yLHMkJM3fA"}}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab7e7fd0018kA9yLHMkJM3fA","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","agent":"build","time":{"created":1769437915134},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","tokens":{"reasoning":0,"input":88,"output":99,"cache":{"write":0,"read":12928}},"finish":"stop"}}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab7e7fd0018kA9yLHMkJM3fA","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","agent":"build","time":{"created":1769437915134,"completed":1769437918003},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","tokens":{"reasoning":0,"input":88,"output":99,"cache":{"write":0,"read":12928}},"finish":"stop"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.idle","properties":{"sessionID":"ses_40549268dffehKpDd14uHIMQaT"}},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"title":"Ask me two questions","diffs":[{"before":"[DEBUG ERROR] git show be64b6205f0da4c9240df2476dc35495429161b5:tests/data/multiple-question-ask.json failed: fatal: path 'tests/data/multiple-question-ask.json' exists on disk, but not in 'be64b6205f0da4c9240df2476dc35495429161b5'\n","after":"[{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437845874},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\\\"}\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4002ydMd2JojY4Nda0\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"\\n00001| local state = require('opencode.state')\\n00002| local ui = require('opencode.ui.ui')\\n00003| local helpers = require('tests.helpers')\\n00004| local output_window = require('opencode.ui.output_window')\\n00005| local assert = require('luassert')\\n00006| local config = require('opencode.config')\\n00007| \\n00008| local function assert_output_matches(expected, actual, name)\\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\\n00010| \\n00011| assert.are.equal(\\n00012| #expected.lines,\\n00013| #actual.lines,\\n00014| string.format(\\n00015| 'Line count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00016| #expected.lines,\\n00017| #actual.lines,\\n00018| math.min(#expected.lines, #actual.lines) + 1,\\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\\n00021| )\\n00022| )\\n00023| \\n00024| for i = 1, #expected.lines do\\n00025| assert.are.equal(\\n00026| expected.lines[i],\\n00027| actual.lines[i],\\n00028| string.format(\\n00029| 'Line %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00030| i,\\n00031| vim.inspect(expected.lines[i]),\\n00032| vim.inspect(actual.lines[i])\\n00033| )\\n00034| )\\n00035| end\\n00036| \\n00037| assert.are.equal(\\n00038| #expected.extmarks,\\n00039| #normalized_extmarks,\\n00040| string.format(\\n00041| 'Extmark count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00042| #expected.extmarks,\\n00043| #normalized_extmarks,\\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\\n00047| )\\n00048| )\\n00049| \\n00050| for i = 1, #expected.extmarks do\\n00051| assert.are.same(\\n00052| expected.extmarks[i],\\n00053| normalized_extmarks[i],\\n00054| string.format(\\n00055| 'Extmark %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00056| i,\\n00057| vim.inspect(expected.extmarks[i]),\\n00058| vim.inspect(normalized_extmarks[i])\\n00059| )\\n00060| )\\n00061| end\\n00062| \\n00063| local expected_action_count = expected.actions and #expected.actions or 0\\n00064| local actual_action_count = actual.actions and #actual.actions or 0\\n00065| \\n00066| assert.are.equal(\\n00067| expected_action_count,\\n00068| actual_action_count,\\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\\n00070| )\\n00071| \\n00072| if expected.actions then\\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\\n00074| local function sort_actions(actions)\\n00075| local sorted = vim.deepcopy(actions)\\n00076| table.sort(sorted, function(a, b)\\n00077| return vim.inspect(a) < vim.inspect(b)\\n00078| end)\\n00079| return sorted\\n00080| end\\n00081| \\n00082| assert.same(\\n00083| sort_actions(expected.actions),\\n00084| sort_actions(actual.actions),\\n00085| string.format(\\n00086| 'Actions mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00087| vim.inspect(expected.actions),\\n00088| vim.inspect(actual.actions)\\n00089| )\\n00090| )\\n00091| end\\n00092| end\\n00093| \\n00094| describe('renderer unit tests', function()\\n00095| local event_subscriptions = {\\n00096| 'session.updated',\\n00097| 'session.compacted',\\n00098| 'session.error',\\n00099| 'message.updated',\\n00100| 'message.removed',\\n00101| 'message.part.updated',\\n00102| 'message.part.removed',\\n00103| 'permission.updated',\\n00104| 'permission.replied',\\n00105| 'question.replied',\\n00106| 'question.asked',\\n00107| 'file.edited',\\n00108| 'custom.restore_point.created',\\n00109| 'custom.emit_events.finished',\\n00110| }\\n00111| \\n00112| before_each(function()\\n00113| require('opencode.event_manager').setup()\\n00114| end)\\n00115| \\n00116| it('subsribes to events correctly', function()\\n00117| local renderer = require('opencode.ui.renderer')\\n00118| local event_manager = state.event_manager\\n00119| \\n00120| event_manager.events = {}\\n00121| \\n00122| renderer.setup_subscriptions()\\n00123| \\n00124| for _, event_name in ipairs(event_subscriptions) do\\n00125| assert.is_true(\\n00126| event_manager.events[event_name] ~= nil,\\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\\n00128| )\\n00129| end\\n00130| end)\\n00131| \\n00132| it('unsubsribes from events correctly', function()\\n00133| local renderer = require('opencode.ui.renderer')\\n00134| local event_manager = state.event_manager\\n00135| \\n00136| renderer.setup_subscriptions()\\n00137| \\n00138| renderer.setup_subscriptions(false)\\n00139| \\n00140| for _, event_name in ipairs(event_subscriptions) do\\n00141| assert.is_true(\\n00142| vim.tbl_isempty(event_manager.events[event_name]),\\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\\n00144| )\\n00145| end\\n00146| end)\\n00147| end)\\n00148| \\n00149| describe('renderer functional tests', function()\\n00150| config.debug.show_ids = true\\n00151| \\n00152| before_each(function()\\n00153| helpers.replay_setup()\\n00154| end)\\n00155| \\n00156| after_each(function()\\n00157| if state.windows then\\n00158| ui.close_windows(state.windows)\\n00159| end\\n00160| end)\\n00161| \\n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\\n00163| \\n00164| -- Don't do the full session test on these files, usually\\n00165| -- because they involve permission prompts\\n00166| local skip_full_session = {\\n00167| 'permission-prompt',\\n00168| 'permission-ask-new',\\n00169| 'question-ask',\\n00170| 'shifting-and-multiple-perms',\\n00171| 'message-removal',\\n00172| }\\n00173| \\n00174| for _, filepath in ipairs(json_files) do\\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\\n00176| \\n00177| if not name:match('%.expected$') then\\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\\n00179| \\n00180| if vim.fn.filereadable(expected_path) == 1 then\\n00181| for i = 1, 2 do\\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\\n00183| it(\\n00184| 'replays '\\n00185| .. name\\n00186| .. ' correctly (event-by-event, '\\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\\n00188| .. ')',\\n00189| function()\\n00190| local events = helpers.load_test_data(filepath)\\n00191| state.active_session = helpers.get_session_from_events(events)\\n00192| local expected = helpers.load_test_data(expected_path)\\n00193| \\n00194| helpers.replay_events(events)\\n00195| vim.wait(1000, function()\\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\\n00197| end)\\n00198| \\n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00200| assert_output_matches(expected, actual, name)\\n00201| end\\n00202| )\\n00203| end\\n00204| \\n00205| if not vim.tbl_contains(skip_full_session, name) then\\n00206| it('replays ' .. name .. ' correctly (session)', function()\\n00207| local renderer = require('opencode.ui.renderer')\\n00208| local events = helpers.load_test_data(filepath)\\n00209| state.active_session = helpers.get_session_from_events(events, true)\\n00210| local expected = helpers.load_test_data(expected_path)\\n00211| \\n00212| local session_data = helpers.load_session_from_events(events)\\n00213| renderer._render_full_session_data(session_data)\\n00214| \\n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00216| assert_output_matches(expected, actual, name)\\n00217| end)\\n00218| end\\n00219| end\\n00220| end\\n00221| end\\n00222| end)\\n00223| \\n\\n(End of file - total 223 lines)\\n\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf80012C7P5ugGXl7Kbv\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"mime\":\"text/plain\",\"type\":\"file\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"id\":\"prt_bfab6dbf9001Kebk9O9AoUc4yI\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"filename\":\"tests/replay/renderer_spec.lua\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"can you use the question tool and ask me a couple of questions\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4001xrYv3BzlgWGgkv\"}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437846547},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"modelID\":\"gpt-4.1\",\"providerID\":\"github-copilot\",\"cost\":0,\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"mode\":\"build\",\"time\":{\"created\":1769437846657},\"role\":\"assistant\",\"path\":{\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"tokens\":{\"reasoning\":0,\"input\":0,\"output\":0,\"cache\":{\"write\":0,\"read\":0}},\"parentID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"step-start\",\"snapshot\":\"be64b6205f0da4c9240df2476dc35495429161b5\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"id\":\"prt_bfab6e0c10013qo2ZThCt7FqLA\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"raw\":\"\",\"input\":{},\"status\":\"pending\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"question.asked\",\"properties\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}],\"id\":\"que_bfab6ef4f0010bzEmrR38ndiIA\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"tool\":{\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"input\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}]},\"time\":{\"start\":1769437851471},\"status\":\"running\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"title\":\"Ask me two questions\",\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"Ask questions about renderer_spec.lua\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]}]\n","deletions":0,"additions":1,"file":"tests/data/multiple-question-ask.json"}]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Ask questions about renderer_spec.lua","version":"1.1.31","summary":{"additions":1,"deletions":0,"files":1},"time":{"created":1769437845874,"updated":1769437918033}}}},{"type":"custom.emit_events.finished","properties":[]}] \ No newline at end of file +[ + { + "type": "session.updated", + "properties": { + "info": { + "slug": "happy-harbor", + "time": { "created": 1769437845874, "updated": 1769437845874 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "id": "ses_40549268dffehKpDd14uHIMQaT", + "version": "1.1.31", + "title": "New session - 2026-01-26T14:30:45.874Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf4002ydMd2JojY4Nda0", + "synthetic": true + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf80012C7P5ugGXl7Kbv", + "synthetic": true + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "mime": "text/plain", + "type": "file", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "id": "prt_bfab6dbf9001Kebk9O9AoUc4yI", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "filename": "tests/replay/renderer_spec.lua", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "can you use the question tool and ask me a couple of questions", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf4001xrYv3BzlgWGgkv" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "slug": "happy-harbor", + "time": { "created": 1769437845874, "updated": 1769437846547 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "id": "ses_40549268dffehKpDd14uHIMQaT", + "version": "1.1.31", + "title": "New session - 2026-01-26T14:30:45.874Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dc80001FueCN7E2691J2R", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "time": { "created": 1769437846657 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2026-01-26T14:30:45.874Z", + "version": "1.1.31", + "summary": { "additions": 0, "deletions": 0, "files": 0 }, + "time": { "created": 1769437845874, "updated": 1769437846728 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "be64b6205f0da4c9240df2476dc35495429161b5", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "id": "prt_bfab6e0c10013qo2ZThCt7FqLA" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab6e0c4001ou3kcu0UICsKQq", + "tool": "question", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "type": "tool", + "state": { "raw": "", "input": {}, "status": "pending" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.asked", + "properties": { + "questions": [ + { + "question": "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "header": "Testing Framework?", + "options": [ + { "label": "busted", "description": "Busted is the most common Lua testing framework." }, + { "label": "plenary.nvim", "description": "Plenary.nvim provides unit test utilities for Neovim plugins." }, + { "label": "other", "description": "Other or custom test framework" } + ] + }, + { + "question": "How do you usually run your tests in this codebase?", + "header": "Run Tests Approach", + "options": [ + { "label": "run_tests.sh (Recommended)", "description": "Use run_tests.sh script provided in repo root." }, + { + "label": "Direct test runner", + "description": "Use individual test commands (e.g., invoke Busted/Plenary directly)." + }, + { "label": "Other", "description": "Other custom scripts or workflow" } + ] + }, + { + "question": "The most important question of all:\nWhich team are you on?", + "header": "multi-line question", + "options": [ + { "label": "Coffee", "description": "#teamcoffee" }, + { "label": "Tea", "description": "#teamtea" } + ] + } + ], + "id": "que_bfab6ef4f0010bzEmrR38ndiIA", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "tool": { "messageID": "msg_bfab6dc80001FueCN7E2691J2R", "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj" } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab6e0c4001ou3kcu0UICsKQq", + "tool": "question", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "type": "tool", + "state": { + "input": { + "questions": [ + { + "question": "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "header": "Testing Framework?", + "options": [ + { "label": "busted", "description": "Busted is the most common Lua testing framework." }, + { + "label": "plenary.nvim", + "description": "Plenary.nvim provides unit test utilities for Neovim plugins." + }, + { "label": "other", "description": "Other or custom test framework" } + ] + }, + { + "question": "How do you usually run your tests in this codebase?", + "header": "Run Tests Approach", + "options": [ + { + "label": "run_tests.sh (Recommended)", + "description": "Use run_tests.sh script provided in repo root." + }, + { + "label": "Direct test runner", + "description": "Use individual test commands (e.g., invoke Busted/Plenary directly)." + }, + { "label": "Other", "description": "Other custom scripts or workflow" } + ] + }, + { + "question": "The most important question of all:\nWhich team are you on?", + "header": "multi-line question", + "options": [ + { "label": "Coffee", "description": "#teamcoffee" }, + { "label": "Tea", "description": "#teamtea" } + ] + } + ] + }, + "time": { "start": 1769437851471 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { "title": "Ask me two questions", "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Ask questions about renderer_spec.lua", + "version": "1.1.31", + "summary": { "additions": 0, "deletions": 0, "files": 0 }, + "time": { "created": 1769437845874, "updated": 1769437846728 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.replied", + "properties": { + "requestID": "que_bfab6ef4f0010bzEmrR38ndiIA", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "answers": [["busted"], ["run_tests.sh (Recommended)"], ["Coffee"]] + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab6e0c4001ou3kcu0UICsKQq", + "tool": "question", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "type": "tool", + "state": { + "metadata": { "answers": [["busted"], ["run_tests.sh (Recommended)"], ["Coffee"]], "truncated": false }, + "output": "User has answered your questions: \"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\"=\"busted\", \"How do you usually run your tests in this codebase?\"=\"run_tests.sh (Recommended)\". You can now continue with the user's answers in mind.", + "time": { "start": 1769437851471, "end": 1769437915067 }, + "input": { + "questions": [ + { + "question": "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "header": "Testing Framework?", + "options": [ + { "label": "busted", "description": "Busted is the most common Lua testing framework." }, + { + "label": "plenary.nvim", + "description": "Plenary.nvim provides unit test utilities for Neovim plugins." + }, + { "label": "other", "description": "Other or custom test framework" } + ] + }, + { + "question": "How do you usually run your tests in this codebase?", + "header": "Run Tests Approach", + "options": [ + { + "label": "run_tests.sh (Recommended)", + "description": "Use run_tests.sh script provided in repo root." + }, + { + "label": "Direct test runner", + "description": "Use individual test commands (e.g., invoke Busted/Plenary directly)." + }, + { "label": "Other", "description": "Other custom scripts or workflow" } + ] + }, + { + "question": "The most important question of all:\nWhich team are you on?", + "header": "multi-line question", + "options": [ + { "label": "Coffee", "description": "#teamcoffee" }, + { "label": "Tea", "description": "#teamtea" } + ] + } + ] + }, + "title": "Asked 3 questions", + "status": "completed" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab7e7c1001GZ9QrtUEO7l0zh", + "type": "step-finish", + "snapshot": "bef5baca5e1828492c87ba3e7d702707c782f6ec", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "cost": 0, + "reason": "tool-calls", + "tokens": { "reasoning": 0, "input": 2789, "output": 176, "cache": { "write": 0, "read": 9984 } }, + "messageID": "msg_bfab6dc80001FueCN7E2691J2R" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dc80001FueCN7E2691J2R", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "agent": "build", + "time": { "created": 1769437846657 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "tokens": { "reasoning": 0, "input": 2789, "output": 176, "cache": { "write": 0, "read": 9984 } }, + "finish": "tool-calls" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "patch", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "id": "prt_bfab7e7f30018PKHf93IEAI3AX", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "hash": "be64b6205f0da4c9240df2476dc35495429161b5", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/tests/data/multiple-question-ask.json"] + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dc80001FueCN7E2691J2R", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "agent": "build", + "time": { "created": 1769437846657, "completed": 1769437915127 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "tokens": { "reasoning": 0, "input": 2789, "output": 176, "cache": { "write": 0, "read": 9984 } }, + "finish": "tool-calls" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "time": { "created": 1769437915134 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { + "title": "Ask me two questions", + "diffs": [ + { + "before": "[DEBUG ERROR] git show be64b6205f0da4c9240df2476dc35495429161b5:tests/data/multiple-question-ask.json failed: fatal: path 'tests/data/multiple-question-ask.json' exists on disk, but not in 'be64b6205f0da4c9240df2476dc35495429161b5'\n", + "after": "[{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437845874},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\\\"}\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4002ydMd2JojY4Nda0\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"\\n00001| local state = require('opencode.state')\\n00002| local ui = require('opencode.ui.ui')\\n00003| local helpers = require('tests.helpers')\\n00004| local output_window = require('opencode.ui.output_window')\\n00005| local assert = require('luassert')\\n00006| local config = require('opencode.config')\\n00007| \\n00008| local function assert_output_matches(expected, actual, name)\\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\\n00010| \\n00011| assert.are.equal(\\n00012| #expected.lines,\\n00013| #actual.lines,\\n00014| string.format(\\n00015| 'Line count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00016| #expected.lines,\\n00017| #actual.lines,\\n00018| math.min(#expected.lines, #actual.lines) + 1,\\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\\n00021| )\\n00022| )\\n00023| \\n00024| for i = 1, #expected.lines do\\n00025| assert.are.equal(\\n00026| expected.lines[i],\\n00027| actual.lines[i],\\n00028| string.format(\\n00029| 'Line %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00030| i,\\n00031| vim.inspect(expected.lines[i]),\\n00032| vim.inspect(actual.lines[i])\\n00033| )\\n00034| )\\n00035| end\\n00036| \\n00037| assert.are.equal(\\n00038| #expected.extmarks,\\n00039| #normalized_extmarks,\\n00040| string.format(\\n00041| 'Extmark count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00042| #expected.extmarks,\\n00043| #normalized_extmarks,\\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\\n00047| )\\n00048| )\\n00049| \\n00050| for i = 1, #expected.extmarks do\\n00051| assert.are.same(\\n00052| expected.extmarks[i],\\n00053| normalized_extmarks[i],\\n00054| string.format(\\n00055| 'Extmark %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00056| i,\\n00057| vim.inspect(expected.extmarks[i]),\\n00058| vim.inspect(normalized_extmarks[i])\\n00059| )\\n00060| )\\n00061| end\\n00062| \\n00063| local expected_action_count = expected.actions and #expected.actions or 0\\n00064| local actual_action_count = actual.actions and #actual.actions or 0\\n00065| \\n00066| assert.are.equal(\\n00067| expected_action_count,\\n00068| actual_action_count,\\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\\n00070| )\\n00071| \\n00072| if expected.actions then\\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\\n00074| local function sort_actions(actions)\\n00075| local sorted = vim.deepcopy(actions)\\n00076| table.sort(sorted, function(a, b)\\n00077| return vim.inspect(a) < vim.inspect(b)\\n00078| end)\\n00079| return sorted\\n00080| end\\n00081| \\n00082| assert.same(\\n00083| sort_actions(expected.actions),\\n00084| sort_actions(actual.actions),\\n00085| string.format(\\n00086| 'Actions mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00087| vim.inspect(expected.actions),\\n00088| vim.inspect(actual.actions)\\n00089| )\\n00090| )\\n00091| end\\n00092| end\\n00093| \\n00094| describe('renderer unit tests', function()\\n00095| local event_subscriptions = {\\n00096| 'session.updated',\\n00097| 'session.compacted',\\n00098| 'session.error',\\n00099| 'message.updated',\\n00100| 'message.removed',\\n00101| 'message.part.updated',\\n00102| 'message.part.removed',\\n00103| 'permission.updated',\\n00104| 'permission.replied',\\n00105| 'question.replied',\\n00106| 'question.asked',\\n00107| 'file.edited',\\n00108| 'custom.restore_point.created',\\n00109| 'custom.emit_events.finished',\\n00110| }\\n00111| \\n00112| before_each(function()\\n00113| require('opencode.event_manager').setup()\\n00114| end)\\n00115| \\n00116| it('subsribes to events correctly', function()\\n00117| local renderer = require('opencode.ui.renderer')\\n00118| local event_manager = state.event_manager\\n00119| \\n00120| event_manager.events = {}\\n00121| \\n00122| renderer.setup_subscriptions()\\n00123| \\n00124| for _, event_name in ipairs(event_subscriptions) do\\n00125| assert.is_true(\\n00126| event_manager.events[event_name] ~= nil,\\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\\n00128| )\\n00129| end\\n00130| end)\\n00131| \\n00132| it('unsubsribes from events correctly', function()\\n00133| local renderer = require('opencode.ui.renderer')\\n00134| local event_manager = state.event_manager\\n00135| \\n00136| renderer.setup_subscriptions()\\n00137| \\n00138| renderer.setup_subscriptions(false)\\n00139| \\n00140| for _, event_name in ipairs(event_subscriptions) do\\n00141| assert.is_true(\\n00142| vim.tbl_isempty(event_manager.events[event_name]),\\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\\n00144| )\\n00145| end\\n00146| end)\\n00147| end)\\n00148| \\n00149| describe('renderer functional tests', function()\\n00150| config.debug.show_ids = true\\n00151| \\n00152| before_each(function()\\n00153| helpers.replay_setup()\\n00154| end)\\n00155| \\n00156| after_each(function()\\n00157| if state.windows then\\n00158| ui.close_windows(state.windows)\\n00159| end\\n00160| end)\\n00161| \\n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\\n00163| \\n00164| -- Don't do the full session test on these files, usually\\n00165| -- because they involve permission prompts\\n00166| local skip_full_session = {\\n00167| 'permission-prompt',\\n00168| 'permission-ask-new',\\n00169| 'question-ask',\\n00170| 'shifting-and-multiple-perms',\\n00171| 'message-removal',\\n00172| }\\n00173| \\n00174| for _, filepath in ipairs(json_files) do\\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\\n00176| \\n00177| if not name:match('%.expected$') then\\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\\n00179| \\n00180| if vim.fn.filereadable(expected_path) == 1 then\\n00181| for i = 1, 2 do\\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\\n00183| it(\\n00184| 'replays '\\n00185| .. name\\n00186| .. ' correctly (event-by-event, '\\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\\n00188| .. ')',\\n00189| function()\\n00190| local events = helpers.load_test_data(filepath)\\n00191| state.active_session = helpers.get_session_from_events(events)\\n00192| local expected = helpers.load_test_data(expected_path)\\n00193| \\n00194| helpers.replay_events(events)\\n00195| vim.wait(1000, function()\\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\\n00197| end)\\n00198| \\n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00200| assert_output_matches(expected, actual, name)\\n00201| end\\n00202| )\\n00203| end\\n00204| \\n00205| if not vim.tbl_contains(skip_full_session, name) then\\n00206| it('replays ' .. name .. ' correctly (session)', function()\\n00207| local renderer = require('opencode.ui.renderer')\\n00208| local events = helpers.load_test_data(filepath)\\n00209| state.active_session = helpers.get_session_from_events(events, true)\\n00210| local expected = helpers.load_test_data(expected_path)\\n00211| \\n00212| local session_data = helpers.load_session_from_events(events)\\n00213| renderer._render_full_session_data(session_data)\\n00214| \\n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00216| assert_output_matches(expected, actual, name)\\n00217| end)\\n00218| end\\n00219| end\\n00220| end\\n00221| end\\n00222| end)\\n00223| \\n\\n(End of file - total 223 lines)\\n\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf80012C7P5ugGXl7Kbv\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"mime\":\"text/plain\",\"type\":\"file\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"id\":\"prt_bfab6dbf9001Kebk9O9AoUc4yI\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"filename\":\"tests/replay/renderer_spec.lua\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"can you use the question tool and ask me a couple of questions\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4001xrYv3BzlgWGgkv\"}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437846547},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"modelID\":\"gpt-4.1\",\"providerID\":\"github-copilot\",\"cost\":0,\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"mode\":\"build\",\"time\":{\"created\":1769437846657},\"role\":\"assistant\",\"path\":{\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"tokens\":{\"reasoning\":0,\"input\":0,\"output\":0,\"cache\":{\"write\":0,\"read\":0}},\"parentID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"step-start\",\"snapshot\":\"be64b6205f0da4c9240df2476dc35495429161b5\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"id\":\"prt_bfab6e0c10013qo2ZThCt7FqLA\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"raw\":\"\",\"input\":{},\"status\":\"pending\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"question.asked\",\"properties\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}],\"id\":\"que_bfab6ef4f0010bzEmrR38ndiIA\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"tool\":{\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"input\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}]},\"time\":{\"start\":1769437851471},\"status\":\"running\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"title\":\"Ask me two questions\",\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"Ask questions about renderer_spec.lua\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]}]\n", + "deletions": 0, + "additions": 1, + "file": "tests/data/multiple-question-ask.json" + } + ] + }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Ask questions about renderer_spec.lua", + "version": "1.1.31", + "summary": { "additions": 1, "deletions": 0, "files": 1 }, + "time": { "created": 1769437845874, "updated": 1769437915176 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "bef5baca5e1828492c87ba3e7d702707c782f6ec", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb3f001iDZa7Ffqv7nqd7" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " answers" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " framework" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n-", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "-" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "_tests" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "I" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " any" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " or" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " tasks" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neov", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "ov" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase.", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "." + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " have" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " or" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " as" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification,", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "," + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " debugging" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " let" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " tailor" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " preferences" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": " for" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437915970 } + }, + "delta": "!" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Thanks for your answers! You've indicated:\n\n- Preferred testing framework: **busted**\n- Typical test running method: **run_tests.sh (Recommended)**\n\nI have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "id": "prt_bfab7eb42001Vm8SgJBX4UFE0B", + "time": { "start": 1769437917971, "end": 1769437917971 } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab7f314001BD9Kv6fskeX4QN", + "type": "step-finish", + "snapshot": "bef5baca5e1828492c87ba3e7d702707c782f6ec", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "cost": 0, + "reason": "stop", + "tokens": { "reasoning": 0, "input": 88, "output": 99, "cache": { "write": 0, "read": 12928 } }, + "messageID": "msg_bfab7e7fd0018kA9yLHMkJM3fA" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "agent": "build", + "time": { "created": 1769437915134 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "tokens": { "reasoning": 0, "input": 88, "output": 99, "cache": { "write": 0, "read": 12928 } }, + "finish": "stop" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab7e7fd0018kA9yLHMkJM3fA", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "agent": "build", + "time": { "created": 1769437915134, "completed": 1769437918003 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "tokens": { "reasoning": 0, "input": 88, "output": 99, "cache": { "write": 0, "read": 12928 } }, + "finish": "stop" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "session.idle", "properties": { "sessionID": "ses_40549268dffehKpDd14uHIMQaT" } }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { + "title": "Ask me two questions", + "diffs": [ + { + "before": "[DEBUG ERROR] git show be64b6205f0da4c9240df2476dc35495429161b5:tests/data/multiple-question-ask.json failed: fatal: path 'tests/data/multiple-question-ask.json' exists on disk, but not in 'be64b6205f0da4c9240df2476dc35495429161b5'\n", + "after": "[{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437845874},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\\\"}\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4002ydMd2JojY4Nda0\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"\\n00001| local state = require('opencode.state')\\n00002| local ui = require('opencode.ui.ui')\\n00003| local helpers = require('tests.helpers')\\n00004| local output_window = require('opencode.ui.output_window')\\n00005| local assert = require('luassert')\\n00006| local config = require('opencode.config')\\n00007| \\n00008| local function assert_output_matches(expected, actual, name)\\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\\n00010| \\n00011| assert.are.equal(\\n00012| #expected.lines,\\n00013| #actual.lines,\\n00014| string.format(\\n00015| 'Line count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00016| #expected.lines,\\n00017| #actual.lines,\\n00018| math.min(#expected.lines, #actual.lines) + 1,\\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\\n00021| )\\n00022| )\\n00023| \\n00024| for i = 1, #expected.lines do\\n00025| assert.are.equal(\\n00026| expected.lines[i],\\n00027| actual.lines[i],\\n00028| string.format(\\n00029| 'Line %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00030| i,\\n00031| vim.inspect(expected.lines[i]),\\n00032| vim.inspect(actual.lines[i])\\n00033| )\\n00034| )\\n00035| end\\n00036| \\n00037| assert.are.equal(\\n00038| #expected.extmarks,\\n00039| #normalized_extmarks,\\n00040| string.format(\\n00041| 'Extmark count mismatch: expected %d, got %d.\\\\nFirst difference at index %d:\\\\n Expected: %s\\\\n Actual: %s',\\n00042| #expected.extmarks,\\n00043| #normalized_extmarks,\\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\\n00047| )\\n00048| )\\n00049| \\n00050| for i = 1, #expected.extmarks do\\n00051| assert.are.same(\\n00052| expected.extmarks[i],\\n00053| normalized_extmarks[i],\\n00054| string.format(\\n00055| 'Extmark %d mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00056| i,\\n00057| vim.inspect(expected.extmarks[i]),\\n00058| vim.inspect(normalized_extmarks[i])\\n00059| )\\n00060| )\\n00061| end\\n00062| \\n00063| local expected_action_count = expected.actions and #expected.actions or 0\\n00064| local actual_action_count = actual.actions and #actual.actions or 0\\n00065| \\n00066| assert.are.equal(\\n00067| expected_action_count,\\n00068| actual_action_count,\\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\\n00070| )\\n00071| \\n00072| if expected.actions then\\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\\n00074| local function sort_actions(actions)\\n00075| local sorted = vim.deepcopy(actions)\\n00076| table.sort(sorted, function(a, b)\\n00077| return vim.inspect(a) < vim.inspect(b)\\n00078| end)\\n00079| return sorted\\n00080| end\\n00081| \\n00082| assert.same(\\n00083| sort_actions(expected.actions),\\n00084| sort_actions(actual.actions),\\n00085| string.format(\\n00086| 'Actions mismatch:\\\\n Expected: %s\\\\n Actual: %s',\\n00087| vim.inspect(expected.actions),\\n00088| vim.inspect(actual.actions)\\n00089| )\\n00090| )\\n00091| end\\n00092| end\\n00093| \\n00094| describe('renderer unit tests', function()\\n00095| local event_subscriptions = {\\n00096| 'session.updated',\\n00097| 'session.compacted',\\n00098| 'session.error',\\n00099| 'message.updated',\\n00100| 'message.removed',\\n00101| 'message.part.updated',\\n00102| 'message.part.removed',\\n00103| 'permission.updated',\\n00104| 'permission.replied',\\n00105| 'question.replied',\\n00106| 'question.asked',\\n00107| 'file.edited',\\n00108| 'custom.restore_point.created',\\n00109| 'custom.emit_events.finished',\\n00110| }\\n00111| \\n00112| before_each(function()\\n00113| require('opencode.event_manager').setup()\\n00114| end)\\n00115| \\n00116| it('subsribes to events correctly', function()\\n00117| local renderer = require('opencode.ui.renderer')\\n00118| local event_manager = state.event_manager\\n00119| \\n00120| event_manager.events = {}\\n00121| \\n00122| renderer.setup_subscriptions()\\n00123| \\n00124| for _, event_name in ipairs(event_subscriptions) do\\n00125| assert.is_true(\\n00126| event_manager.events[event_name] ~= nil,\\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\\n00128| )\\n00129| end\\n00130| end)\\n00131| \\n00132| it('unsubsribes from events correctly', function()\\n00133| local renderer = require('opencode.ui.renderer')\\n00134| local event_manager = state.event_manager\\n00135| \\n00136| renderer.setup_subscriptions()\\n00137| \\n00138| renderer.setup_subscriptions(false)\\n00139| \\n00140| for _, event_name in ipairs(event_subscriptions) do\\n00141| assert.is_true(\\n00142| vim.tbl_isempty(event_manager.events[event_name]),\\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\\n00144| )\\n00145| end\\n00146| end)\\n00147| end)\\n00148| \\n00149| describe('renderer functional tests', function()\\n00150| config.debug.show_ids = true\\n00151| \\n00152| before_each(function()\\n00153| helpers.replay_setup()\\n00154| end)\\n00155| \\n00156| after_each(function()\\n00157| if state.windows then\\n00158| ui.close_windows(state.windows)\\n00159| end\\n00160| end)\\n00161| \\n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\\n00163| \\n00164| -- Don't do the full session test on these files, usually\\n00165| -- because they involve permission prompts\\n00166| local skip_full_session = {\\n00167| 'permission-prompt',\\n00168| 'permission-ask-new',\\n00169| 'question-ask',\\n00170| 'shifting-and-multiple-perms',\\n00171| 'message-removal',\\n00172| }\\n00173| \\n00174| for _, filepath in ipairs(json_files) do\\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\\n00176| \\n00177| if not name:match('%.expected$') then\\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\\n00179| \\n00180| if vim.fn.filereadable(expected_path) == 1 then\\n00181| for i = 1, 2 do\\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\\n00183| it(\\n00184| 'replays '\\n00185| .. name\\n00186| .. ' correctly (event-by-event, '\\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\\n00188| .. ')',\\n00189| function()\\n00190| local events = helpers.load_test_data(filepath)\\n00191| state.active_session = helpers.get_session_from_events(events)\\n00192| local expected = helpers.load_test_data(expected_path)\\n00193| \\n00194| helpers.replay_events(events)\\n00195| vim.wait(1000, function()\\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\\n00197| end)\\n00198| \\n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00200| assert_output_matches(expected, actual, name)\\n00201| end\\n00202| )\\n00203| end\\n00204| \\n00205| if not vim.tbl_contains(skip_full_session, name) then\\n00206| it('replays ' .. name .. ' correctly (session)', function()\\n00207| local renderer = require('opencode.ui.renderer')\\n00208| local events = helpers.load_test_data(filepath)\\n00209| state.active_session = helpers.get_session_from_events(events, true)\\n00210| local expected = helpers.load_test_data(expected_path)\\n00211| \\n00212| local session_data = helpers.load_session_from_events(events)\\n00213| renderer._render_full_session_data(session_data)\\n00214| \\n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\\n00216| assert_output_matches(expected, actual, name)\\n00217| end)\\n00218| end\\n00219| end\\n00220| end\\n00221| end\\n00222| end)\\n00223| \\n\\n(End of file - total 223 lines)\\n\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf80012C7P5ugGXl7Kbv\",\"synthetic\":true}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"mime\":\"text/plain\",\"type\":\"file\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"id\":\"prt_bfab6dbf9001Kebk9O9AoUc4yI\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"filename\":\"tests/replay/renderer_spec.lua\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"text\",\"text\":\"can you use the question tool and ask me a couple of questions\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"id\":\"prt_bfab6dbf4001xrYv3BzlgWGgkv\"}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"slug\":\"happy-harbor\",\"time\":{\"created\":1769437845874,\"updated\":1769437846547},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"version\":\"1.1.31\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"modelID\":\"gpt-4.1\",\"providerID\":\"github-copilot\",\"cost\":0,\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"mode\":\"build\",\"time\":{\"created\":1769437846657},\"role\":\"assistant\",\"path\":{\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"tokens\":{\"reasoning\":0,\"input\":0,\"output\":0,\"cache\":{\"write\":0,\"read\":0}},\"parentID\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\"}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"New session - 2026-01-26T14:30:45.874Z\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"type\":\"step-start\",\"snapshot\":\"be64b6205f0da4c9240df2476dc35495429161b5\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"id\":\"prt_bfab6e0c10013qo2ZThCt7FqLA\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"raw\":\"\",\"input\":{},\"status\":\"pending\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"question.asked\",\"properties\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}],\"id\":\"que_bfab6ef4f0010bzEmrR38ndiIA\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"tool\":{\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\"}}},{\"type\":\"message.part.updated\",\"properties\":{\"part\":{\"id\":\"prt_bfab6e0c4001ou3kcu0UICsKQq\",\"tool\":\"question\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"callID\":\"call_wHoqSXKi7juWQVtpwv4tMMTj\",\"messageID\":\"msg_bfab6dc80001FueCN7E2691J2R\",\"type\":\"tool\",\"state\":{\"input\":{\"questions\":[{\"question\":\"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?\",\"header\":\"Testing Framework?\",\"options\":[{\"label\":\"busted\",\"description\":\"Busted is the most common Lua testing framework.\"},{\"label\":\"plenary.nvim\",\"description\":\"Plenary.nvim provides unit test utilities for Neovim plugins.\"},{\"label\":\"other\",\"description\":\"Other or custom test framework\"}]},{\"question\":\"How do you usually run your tests in this codebase?\",\"header\":\"Run Tests Approach\",\"options\":[{\"label\":\"run_tests.sh (Recommended)\",\"description\":\"Use run_tests.sh script provided in repo root.\"},{\"label\":\"Direct test runner\",\"description\":\"Use individual test commands (e.g., invoke Busted/Plenary directly).\"},{\"label\":\"Other\",\"description\":\"Other custom scripts or workflow\"}]}]},\"time\":{\"start\":1769437851471},\"status\":\"running\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"message.updated\",\"properties\":{\"info\":{\"id\":\"msg_bfab6dbf3001ZOVHTFKR1CMUE5\",\"time\":{\"created\":1769437846515},\"role\":\"user\",\"agent\":\"build\",\"sessionID\":\"ses_40549268dffehKpDd14uHIMQaT\",\"summary\":{\"title\":\"Ask me two questions\",\"diffs\":[]},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"session.updated\",\"properties\":{\"info\":{\"id\":\"ses_40549268dffehKpDd14uHIMQaT\",\"slug\":\"happy-harbor\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"title\":\"Ask questions about renderer_spec.lua\",\"version\":\"1.1.31\",\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0},\"time\":{\"created\":1769437845874,\"updated\":1769437846728}}}},{\"type\":\"custom.emit_events.finished\",\"properties\":[]},{\"type\":\"custom.emit_events.finished\",\"properties\":[]}]\n", + "deletions": 0, + "additions": 1, + "file": "tests/data/multiple-question-ask.json" + } + ] + }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Ask questions about renderer_spec.lua", + "version": "1.1.31", + "summary": { "additions": 1, "deletions": 0, "files": 1 }, + "time": { "created": 1769437845874, "updated": 1769437918033 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/multiple-question-ask.expected.json b/tests/data/multiple-question-ask.expected.json index aab54fb2..5849dc4b 100644 --- a/tests/data/multiple-question-ask.expected.json +++ b/tests/data/multiple-question-ask.expected.json @@ -1 +1,326 @@ -{"lines":["----","","","[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)","","can you use the question tool and ask me a couple of questions","","----","","","** question** ","","----","",""," Question (1/2)","","Which Lua testing framework do you use or prefer for your Neovim Lua plugins?",""," 1. busted - Busted is the most common Lua testing framework. "," 2. plenary.nvim - Plenary.nvim provides unit test utilities for Neovim plugins."," 3. other - Other or custom test framework","","Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``","",""],"actions":[],"extmarks":[[1,1,0,{"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col","virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-26 14:30:46)","OpencodeHint"],[" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]","OpencodeHint"]],"ns_id":3}],[2,2,0,{"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3}],[3,3,0,{"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3}],[4,4,0,{"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3}],[5,5,0,{"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3}],[6,6,0,{"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3}],[7,8,0,{"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:30:46)","OpencodeHint"],[" [msg_bfab6dc80001FueCN7E2691J2R]","OpencodeHint"]],"ns_id":3}],[8,13,0,{"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleSystem"],[" "],["SYSTEM","OpencodeMessageRoleSystem"],["","OpencodeHint"],["","OpencodeHint"],[" [question-display-message]","OpencodeHint"]],"ns_id":3}],[9,15,0,{"line_hl_group":"OpencodeQuestionTitle","priority":4096,"right_gravity":true,"ns_id":3}],[10,15,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[11,16,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[12,17,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[13,18,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[14,19,0,{"line_hl_group":"OpencodeDialogOptionHover","priority":4096,"right_gravity":true,"ns_id":3}],[15,19,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[16,19,2,{"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":false,"virt_text_pos":"overlay","virt_text":[["› ","OpencodeDialogOptionHover"]],"ns_id":3}],[17,20,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[18,21,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[19,22,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}],[20,23,0,{"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3}]],"timestamp":1769441953} +{ + "lines": [ + "----", + "", + "", + "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", + "", + "can you use the question tool and ask me a couple of questions", + "", + "----", + "", + "", + "** question** ", + "", + "----", + "", + "", + " Question (1/2)", + "", + "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "", + " 1. busted - Busted is the most common Lua testing framework. ", + " 2. plenary.nvim - Plenary.nvim provides unit test utilities for Neovim plugins.", + " 3. other - Other or custom test framework", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``", + "", + "" + ], + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col", + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-26 14:30:46)", "OpencodeHint"], + [" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", "OpencodeHint"] + ], + "ns_id": 3 + } + ], + [ + 2, + 2, + 0, + { + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3 + } + ], + [ + 3, + 3, + 0, + { + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3 + } + ], + [ + 4, + 4, + 0, + { + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3 + } + ], + [ + 5, + 5, + 0, + { + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3 + } + ], + [ + 6, + 6, + 0, + { + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3 + } + ], + [ + 7, + 8, + 0, + { + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:30:46)", "OpencodeHint"], + [" [msg_bfab6dc80001FueCN7E2691J2R]", "OpencodeHint"] + ], + "ns_id": 3 + } + ], + [ + 8, + 13, + 0, + { + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleSystem"], + [" "], + ["SYSTEM", "OpencodeMessageRoleSystem"], + ["", "OpencodeHint"], + ["", "OpencodeHint"], + [" [question-display-message]", "OpencodeHint"] + ], + "ns_id": 3 + } + ], + [9, 15, 0, { "line_hl_group": "OpencodeQuestionTitle", "priority": 4096, "right_gravity": true, "ns_id": 3 }], + [ + 10, + 15, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 11, + 16, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 12, + 17, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 13, + 18, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [14, 19, 0, { "line_hl_group": "OpencodeDialogOptionHover", "priority": 4096, "right_gravity": true, "ns_id": 3 }], + [ + 15, + 19, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 16, + 19, + 2, + { + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "overlay", + "virt_text": [["› ", "OpencodeDialogOptionHover"]], + "ns_id": 3 + } + ], + [ + 17, + 20, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 18, + 21, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 19, + 22, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ], + [ + 20, + 23, + 0, + { + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3 + } + ] + ], + "timestamp": 1769441953 +} diff --git a/tests/data/multiple-question-ask.json b/tests/data/multiple-question-ask.json index 4a4da6ac..5907084d 100644 --- a/tests/data/multiple-question-ask.json +++ b/tests/data/multiple-question-ask.json @@ -1 +1,337 @@ -[{"type":"session.updated","properties":{"info":{"slug":"happy-harbor","time":{"created":1769437845874,"updated":1769437845874},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","id":"ses_40549268dffehKpDd14uHIMQaT","version":"1.1.31","title":"New session - 2026-01-26T14:30:45.874Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf4002ydMd2JojY4Nda0","synthetic":true}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf80012C7P5ugGXl7Kbv","synthetic":true}}},{"type":"message.part.updated","properties":{"part":{"mime":"text/plain","type":"file","sessionID":"ses_40549268dffehKpDd14uHIMQaT","id":"prt_bfab6dbf9001Kebk9O9AoUc4yI","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","filename":"tests/replay/renderer_spec.lua","url":"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"can you use the question tool and ask me a couple of questions","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","id":"prt_bfab6dbf4001xrYv3BzlgWGgkv"}}},{"type":"session.updated","properties":{"info":{"slug":"happy-harbor","time":{"created":1769437845874,"updated":1769437846547},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","id":"ses_40549268dffehKpDd14uHIMQaT","version":"1.1.31","title":"New session - 2026-01-26T14:30:45.874Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dc80001FueCN7E2691J2R","modelID":"gpt-4.1","providerID":"github-copilot","cost":0,"sessionID":"ses_40549268dffehKpDd14uHIMQaT","mode":"build","time":{"created":1769437846657},"role":"assistant","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_bfab6dbf3001ZOVHTFKR1CMUE5"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2026-01-26T14:30:45.874Z","version":"1.1.31","summary":{"additions":0,"deletions":0,"files":0},"time":{"created":1769437845874,"updated":1769437846728}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"be64b6205f0da4c9240df2476dc35495429161b5","sessionID":"ses_40549268dffehKpDd14uHIMQaT","messageID":"msg_bfab6dc80001FueCN7E2691J2R","id":"prt_bfab6e0c10013qo2ZThCt7FqLA"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab6e0c4001ou3kcu0UICsKQq","tool":"question","sessionID":"ses_40549268dffehKpDd14uHIMQaT","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj","messageID":"msg_bfab6dc80001FueCN7E2691J2R","type":"tool","state":{"raw":"","input":{},"status":"pending"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.asked","properties":{"questions":[{"question":"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","header":"Testing Framework?","options":[{"label":"busted","description":"Busted is the most common Lua testing framework."},{"label":"plenary.nvim","description":"Plenary.nvim provides unit test utilities for Neovim plugins."},{"label":"other","description":"Other or custom test framework"}]},{"question":"How do you usually run your tests in this codebase?","header":"Run Tests Approach","options":[{"label":"run_tests.sh (Recommended)","description":"Use run_tests.sh script provided in repo root."},{"label":"Direct test runner","description":"Use individual test commands (e.g., invoke Busted/Plenary directly)."},{"label":"Other","description":"Other custom scripts or workflow"}]}],"id":"que_bfab6ef4f0010bzEmrR38ndiIA","sessionID":"ses_40549268dffehKpDd14uHIMQaT","tool":{"messageID":"msg_bfab6dc80001FueCN7E2691J2R","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfab6e0c4001ou3kcu0UICsKQq","tool":"question","sessionID":"ses_40549268dffehKpDd14uHIMQaT","callID":"call_wHoqSXKi7juWQVtpwv4tMMTj","messageID":"msg_bfab6dc80001FueCN7E2691J2R","type":"tool","state":{"input":{"questions":[{"question":"Which Lua testing framework do you use or prefer for your Neovim Lua plugins?","header":"Testing Framework?","options":[{"label":"busted","description":"Busted is the most common Lua testing framework."},{"label":"plenary.nvim","description":"Plenary.nvim provides unit test utilities for Neovim plugins."},{"label":"other","description":"Other or custom test framework"}]},{"question":"How do you usually run your tests in this codebase?","header":"Run Tests Approach","options":[{"label":"run_tests.sh (Recommended)","description":"Use run_tests.sh script provided in repo root."},{"label":"Direct test runner","description":"Use individual test commands (e.g., invoke Busted/Plenary directly)."},{"label":"Other","description":"Other custom scripts or workflow"}]}]},"time":{"start":1769437851471},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_bfab6dbf3001ZOVHTFKR1CMUE5","time":{"created":1769437846515},"role":"user","agent":"build","sessionID":"ses_40549268dffehKpDd14uHIMQaT","summary":{"title":"Ask me two questions","diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"id":"ses_40549268dffehKpDd14uHIMQaT","slug":"happy-harbor","directory":"/home/francis/Projects/_nvim/opencode.nvim","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Ask questions about renderer_spec.lua","version":"1.1.31","summary":{"additions":0,"deletions":0,"files":0},"time":{"created":1769437845874,"updated":1769437846728}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]}] +[ + { + "type": "session.updated", + "properties": { + "info": { + "slug": "happy-harbor", + "time": { "created": 1769437845874, "updated": 1769437845874 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "id": "ses_40549268dffehKpDd14uHIMQaT", + "version": "1.1.31", + "title": "New session - 2026-01-26T14:30:45.874Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf4002ydMd2JojY4Nda0", + "synthetic": true + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf80012C7P5ugGXl7Kbv", + "synthetic": true + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "mime": "text/plain", + "type": "file", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "id": "prt_bfab6dbf9001Kebk9O9AoUc4yI", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "filename": "tests/replay/renderer_spec.lua", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "can you use the question tool and ask me a couple of questions", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "id": "prt_bfab6dbf4001xrYv3BzlgWGgkv" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "slug": "happy-harbor", + "time": { "created": 1769437845874, "updated": 1769437846547 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "id": "ses_40549268dffehKpDd14uHIMQaT", + "version": "1.1.31", + "title": "New session - 2026-01-26T14:30:45.874Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dc80001FueCN7E2691J2R", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "cost": 0, + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "mode": "build", + "time": { "created": 1769437846657 }, + "role": "assistant", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_bfab6dbf3001ZOVHTFKR1CMUE5" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2026-01-26T14:30:45.874Z", + "version": "1.1.31", + "summary": { "additions": 0, "deletions": 0, "files": 0 }, + "time": { "created": 1769437845874, "updated": 1769437846728 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "be64b6205f0da4c9240df2476dc35495429161b5", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "id": "prt_bfab6e0c10013qo2ZThCt7FqLA" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab6e0c4001ou3kcu0UICsKQq", + "tool": "question", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "type": "tool", + "state": { "raw": "", "input": {}, "status": "pending" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.asked", + "properties": { + "questions": [ + { + "question": "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "header": "Testing Framework?", + "options": [ + { "label": "busted", "description": "Busted is the most common Lua testing framework." }, + { "label": "plenary.nvim", "description": "Plenary.nvim provides unit test utilities for Neovim plugins." }, + { "label": "other", "description": "Other or custom test framework" } + ] + }, + { + "question": "How do you usually run your tests in this codebase?", + "header": "Run Tests Approach", + "options": [ + { "label": "run_tests.sh (Recommended)", "description": "Use run_tests.sh script provided in repo root." }, + { + "label": "Direct test runner", + "description": "Use individual test commands (e.g., invoke Busted/Plenary directly)." + }, + { "label": "Other", "description": "Other custom scripts or workflow" } + ] + } + ], + "id": "que_bfab6ef4f0010bzEmrR38ndiIA", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "tool": { "messageID": "msg_bfab6dc80001FueCN7E2691J2R", "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj" } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfab6e0c4001ou3kcu0UICsKQq", + "tool": "question", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "callID": "call_wHoqSXKi7juWQVtpwv4tMMTj", + "messageID": "msg_bfab6dc80001FueCN7E2691J2R", + "type": "tool", + "state": { + "input": { + "questions": [ + { + "question": "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "header": "Testing Framework?", + "options": [ + { "label": "busted", "description": "Busted is the most common Lua testing framework." }, + { + "label": "plenary.nvim", + "description": "Plenary.nvim provides unit test utilities for Neovim plugins." + }, + { "label": "other", "description": "Other or custom test framework" } + ] + }, + { + "question": "How do you usually run your tests in this codebase?", + "header": "Run Tests Approach", + "options": [ + { + "label": "run_tests.sh (Recommended)", + "description": "Use run_tests.sh script provided in repo root." + }, + { + "label": "Direct test runner", + "description": "Use individual test commands (e.g., invoke Busted/Plenary directly)." + }, + { "label": "Other", "description": "Other custom scripts or workflow" } + ] + } + ] + }, + "time": { "start": 1769437851471 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_bfab6dbf3001ZOVHTFKR1CMUE5", + "time": { "created": 1769437846515 }, + "role": "user", + "agent": "build", + "sessionID": "ses_40549268dffehKpDd14uHIMQaT", + "summary": { "title": "Ask me two questions", "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "id": "ses_40549268dffehKpDd14uHIMQaT", + "slug": "happy-harbor", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Ask questions about renderer_spec.lua", + "version": "1.1.31", + "summary": { "additions": 0, "deletions": 0, "files": 0 }, + "time": { "created": 1769437845874, "updated": 1769437846728 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/multiple-stacked-permissions.json b/tests/data/multiple-stacked-permissions.json index 8990fe32..e7584879 100644 --- a/tests/data/multiple-stacked-permissions.json +++ b/tests/data/multiple-stacked-permissions.json @@ -273,4 +273,4 @@ "messageID": "msg_assistant_response_001" } } -] \ No newline at end of file +] diff --git a/tests/data/perf.expected.json b/tests/data/perf.expected.json index 010280dd..47fc23de 100644 --- a/tests/data/perf.expected.json +++ b/tests/data/perf.expected.json @@ -1 +1,429 @@ -{"extmarks":[[1,1,0,{"virt_text_repeat_linebreak":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-24 19:32:00)","OpencodeHint"],[" [msg_a17b4dc4c001x19oFZANB8CsEB]","OpencodeHint"]],"right_gravity":true,"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}],[2,2,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}],[3,3,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}],[4,4,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}],[5,5,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}],[6,8,0,{"virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-24 19:32:01)","OpencodeHint"],[" [msg_a17b4e166001vCnLczdZXvqLL6]","OpencodeHint"]],"right_gravity":true,"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"ns_id":3,"virt_text_pos":"win_col"}]],"actions":[],"lines":["----","","","I just want to test the markdown rendering, please write as long session with codeblocksto","","[`lua/opencode/ui/output_window.lua`](lua/opencode/ui/output_window.lua)","","----","","","Here's a long markdown-rich session with many fenced code blocks to help test rendering. I include the file you provided plus a variety of smaller snippets in different languages and formats.","","**File Contents**","-  `lua/opencode/ui/output_window.lua:1`","","```lua","--  lua/opencode/ui/output_window.lua","local state = require('opencode.state')","local config = require('opencode.config')","","local M = {}","M.namespace = vim.api.nvim_create_namespace('opencode_output')","","function M.create_buf()"," local output_buf = vim.api.nvim_create_buf(false, true)"," vim.api.nvim_set_option_value('filetype', 'opencode_output', { buf = output_buf })"," return output_buf","end","","function M._build_output_win_config()"," return {"," relative = 'editor',"," width = config.ui.window_width or 80,"," row = 2,"," col = 2,"," style = 'minimal',"," border = 'rounded',"," zindex = 40,"," }","end","","function M.mounted(windows)"," windows = windows or state.windows"," if"," not state.windows"," or not state.windows.output_buf"," or not state.windows.output_win"," or not vim.api.nvim_win_is_valid(windows.output_win)"," then"," return false"," end",""," return true","end","","function M.setup(windows)"," vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight, { win = windows.output_win })"," vim.api.nvim_set_option_value('wrap', true, { win = windows.output_win })"," vim.api.nvim_set_option_value('number', false, { win = windows.output_win })"," vim.api.nvim_set_option_value('relativenumber', false, { win = windows.output_win })"," vim.api.nvim_set_option_value('modifiable', false, { buf = windows.output_buf })"," vim.api.nvim_set_option_value('buftype', 'nofile', { buf = windows.output_buf })"," vim.api.nvim_set_option_value('swapfile', false, { buf = windows.output_buf })"," vim.api.nvim_set_option_value('winfixbuf', true, { win = windows.output_win })"," vim.api.nvim_set_option_value('winfixheight', true, { win = windows.output_win })"," vim.api.nvim_set_option_value('winfixwidth', true, { win = windows.output_win })"," vim.api.nvim_set_option_value('signcolumn', 'yes', { scope = 'local', win = windows.output_win })"," vim.api.nvim_set_option_value('list', false, { scope = 'local', win = windows.output_win })",""," M.update_dimensions(windows)"," M.setup_keymaps(windows)","end","","function M.update_dimensions(windows)"," local total_width = vim.api.nvim_get_option_value('columns', {})"," local width = math.floor(total_width * config.ui.window_width)",""," vim.api.nvim_win_set_config(windows.output_win, { width = width })","end","","function M.get_buf_line_count()"," if not M.mounted() then"," return 0"," end",""," return vim.api.nvim_buf_line_count(state.windows.output_buf)","end","","---Set the output buffer contents","---@param lines string[] The lines to set","---@param start_line? integer The starting line to set, defaults to 0","---@param end_line? integer The last line to set, defaults to -1","function M.set_lines(lines, start_line, end_line)"," if not M.mounted() then"," return"," end",""," start_line = start_line or 0"," end_line = end_line or -1",""," local windows = state.windows"," if not windows or not windows.output_buf then"," return"," end",""," vim.api.nvim_set_option_value('modifiable', true, { buf = windows.output_buf })"," vim.api.nvim_buf_set_lines(windows.output_buf, start_line, end_line, false, lines)"," vim.api.nvim_set_option_value('modifiable', false, { buf = windows.output_buf })","end","","---Clear output buf extmarks","---@param start_line? integer Line to start clearing, defaults 0","---@param end_line? integer Line to to clear until, defaults to -1","function M.clear_extmarks(start_line, end_line)"," if not M.mounted() or not state.windows.output_buf then"," return"," end",""," start_line = start_line or 0"," end_line = end_line or -1",""," vim.api.nvim_buf_clear_namespace(state.windows.output_buf, M.namespace, start_line, end_line)","end","","---Apply extmarks to the output buffer","---@param extmarks table Extmarks indexed by line","---@param line_offset? integer Line offset to apply to extmarks, defaults to 0","function M.set_extmarks(extmarks, line_offset)"," if not M.mounted() or not extmarks or type(extmarks) ~= 'table' then"," return"," end",""," line_offset = line_offset or 0",""," local output_buf = state.windows.output_buf",""," for line_idx, marks in pairs(extmarks) do"," for _, mark in ipairs(marks) do"," local actual_mark = type(mark) == 'function' and mark() or mark"," local target_line = line_offset + line_idx"," if actual_mark.end_row then"," actual_mark.end_row = actual_mark.end_row + line_offset"," end"," local start_col = actual_mark.start_col"," if actual_mark.start_col then"," actual_mark.start_col = nil"," end"," pcall(vim.api.nvim_buf_set_extmark, output_buf, M.namespace, target_line, start_col or 0, actual_mark)"," end"," end","end","","function M.focus_output(should_stop_insert)"," if should_stop_insert then"," vim.cmd('stopinsert')"," end"," vim.api.nvim_set_current_win(state.windows.output_win)","end","","function M.close()"," if M.mounted() then"," return"," end"," pcall(vim.api.nvim_win_close, state.windows.output_win, true)"," pcall(vim.api.nvim_buf_delete, state.windows.output_buf, { force = true })","end","","function M.setup_keymaps(windows)"," local keymap = require('opencode.keymap')"," keymap.setup_window_keymaps(config.keymap.output_window, windows.output_buf)","end","","function M.setup_autocmds(windows, group)"," vim.api.nvim_create_autocmd('WinEnter', {"," group = group,"," buffer = windows.output_buf,"," callback = function()"," vim.cmd('stopinsert')"," state.last_focused_opencode_window = 'output'"," require('opencode.ui.input_window').refresh_placeholder(state.windows)"," end,"," })",""," vim.api.nvim_create_autocmd('BufEnter', {"," group = group,"," buffer = windows.output_buf,"," callback = function()"," vim.cmd('stopinsert')"," state.last_focused_opencode_window = 'output'"," require('opencode.ui.input_window').refresh_placeholder(state.windows)"," end,"," })",""," state.subscribe('current_permission', function()"," require('opencode.keymap').toggle_permission_keymap(windows.output_buf)"," end)","end","","function M.clear()"," M.set_lines({})"," M.clear_extmarks()","end","","return M","```","","**Shell / Commands**","","```bash","# show project root and run tests","pwd","ls -la"," ./run_tests.sh","```","","**Short Lua examples**","","```lua","-- Print buffer line count if mounted","local out = require('opencode.ui.output_window')","if out.mounted() then"," print('Lines:', out.get_buf_line_count())","else"," print('Output window not mounted.')","end","```","","**JSON sample**","","```json","{"," \"name\": \"opencode.nvim\","," \"version\": \"0.0.0\","," \"tests\": {"," \"unit\": 128,"," \"manual\": 12"," }","}","```","","**YAML sample**","","```yaml","ui:"," window_width: 0.5"," window_highlight: \"Normal:NormalFloat\"","keymap:"," output_window:"," close: \"\""," focus: \"\"","```","","**Unified Diff example**","","```diff","*** before/lua/opencode/ui/output_window.lua","@@","- vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight, { win = windows.output_win })","+ vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight or '', { win = windows.output_win })","```","","**Python snippet**","","```python","# small helper to format lines","def format_lines(lines):"," return [line.rstrip() for line in lines if line is not None]","```","","**SQL example**","","```sql","-- List recent sessions","SELECT id, created_at, user_id","FROM sessions","WHERE created_at > now() - interval '7 days'","ORDER BY created_at DESC","LIMIT 10;","```","","**HTML fragment**","","```html","",""," Render Test"," ","
-- sample Lua inside HTML
"," ","","```","","**Indented (classic) code block**",""," This is an indented code block."," It should render as preformatted text without language highlighting.","","**Shell snippet with inline env vars**","","```bash","export OPENCODE_ENV=development","nvim --headless -u  tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = ' ./tests/minimal/init.lua'})\"","```","","**Mixed inline code examples**","- Use backticks for commands:  `./run_tests.sh`","- File path with start line:  `lua/opencode/ui/output_window.lua:1`","- API call: `vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)`","","**Small checklist**","- [x] Lua file included ( `lua/opencode/ui/output_window.lua:1`)","- [x] Multiple fenced code blocks","- [x] Several languages: Lua, Bash, JSON, YAML, Python, SQL, HTML, Diff","","If you want more variations (many short blocks, nested lists, tables, or extremely long blocks), tell me which formats or languages to expand and I’ll produce them.","",""],"timestamp":1769091196} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-24 19:32:00)", "OpencodeHint"], + [" [msg_a17b4dc4c001x19oFZANB8CsEB]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ], + [ + 2, + 2, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ], + [ + 3, + 3, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ], + [ + 4, + 4, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ], + [ + 5, + 5, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ], + [ + 6, + 8, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-24 19:32:01)", "OpencodeHint"], + [" [msg_a17b4e166001vCnLczdZXvqLL6]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "ns_id": 3, + "virt_text_pos": "win_col" + } + ] + ], + "actions": [], + "lines": [ + "----", + "", + "", + "I just want to test the markdown rendering, please write as long session with codeblocksto", + "", + "[`lua/opencode/ui/output_window.lua`](lua/opencode/ui/output_window.lua)", + "", + "----", + "", + "", + "Here's a long markdown-rich session with many fenced code blocks to help test rendering. I include the file you provided plus a variety of smaller snippets in different languages and formats.", + "", + "**File Contents**", + "-  `lua/opencode/ui/output_window.lua:1`", + "", + "```lua", + "--  lua/opencode/ui/output_window.lua", + "local state = require('opencode.state')", + "local config = require('opencode.config')", + "", + "local M = {}", + "M.namespace = vim.api.nvim_create_namespace('opencode_output')", + "", + "function M.create_buf()", + " local output_buf = vim.api.nvim_create_buf(false, true)", + " vim.api.nvim_set_option_value('filetype', 'opencode_output', { buf = output_buf })", + " return output_buf", + "end", + "", + "function M._build_output_win_config()", + " return {", + " relative = 'editor',", + " width = config.ui.window_width or 80,", + " row = 2,", + " col = 2,", + " style = 'minimal',", + " border = 'rounded',", + " zindex = 40,", + " }", + "end", + "", + "function M.mounted(windows)", + " windows = windows or state.windows", + " if", + " not state.windows", + " or not state.windows.output_buf", + " or not state.windows.output_win", + " or not vim.api.nvim_win_is_valid(windows.output_win)", + " then", + " return false", + " end", + "", + " return true", + "end", + "", + "function M.setup(windows)", + " vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight, { win = windows.output_win })", + " vim.api.nvim_set_option_value('wrap', true, { win = windows.output_win })", + " vim.api.nvim_set_option_value('number', false, { win = windows.output_win })", + " vim.api.nvim_set_option_value('relativenumber', false, { win = windows.output_win })", + " vim.api.nvim_set_option_value('modifiable', false, { buf = windows.output_buf })", + " vim.api.nvim_set_option_value('buftype', 'nofile', { buf = windows.output_buf })", + " vim.api.nvim_set_option_value('swapfile', false, { buf = windows.output_buf })", + " vim.api.nvim_set_option_value('winfixbuf', true, { win = windows.output_win })", + " vim.api.nvim_set_option_value('winfixheight', true, { win = windows.output_win })", + " vim.api.nvim_set_option_value('winfixwidth', true, { win = windows.output_win })", + " vim.api.nvim_set_option_value('signcolumn', 'yes', { scope = 'local', win = windows.output_win })", + " vim.api.nvim_set_option_value('list', false, { scope = 'local', win = windows.output_win })", + "", + " M.update_dimensions(windows)", + " M.setup_keymaps(windows)", + "end", + "", + "function M.update_dimensions(windows)", + " local total_width = vim.api.nvim_get_option_value('columns', {})", + " local width = math.floor(total_width * config.ui.window_width)", + "", + " vim.api.nvim_win_set_config(windows.output_win, { width = width })", + "end", + "", + "function M.get_buf_line_count()", + " if not M.mounted() then", + " return 0", + " end", + "", + " return vim.api.nvim_buf_line_count(state.windows.output_buf)", + "end", + "", + "---Set the output buffer contents", + "---@param lines string[] The lines to set", + "---@param start_line? integer The starting line to set, defaults to 0", + "---@param end_line? integer The last line to set, defaults to -1", + "function M.set_lines(lines, start_line, end_line)", + " if not M.mounted() then", + " return", + " end", + "", + " start_line = start_line or 0", + " end_line = end_line or -1", + "", + " local windows = state.windows", + " if not windows or not windows.output_buf then", + " return", + " end", + "", + " vim.api.nvim_set_option_value('modifiable', true, { buf = windows.output_buf })", + " vim.api.nvim_buf_set_lines(windows.output_buf, start_line, end_line, false, lines)", + " vim.api.nvim_set_option_value('modifiable', false, { buf = windows.output_buf })", + "end", + "", + "---Clear output buf extmarks", + "---@param start_line? integer Line to start clearing, defaults 0", + "---@param end_line? integer Line to to clear until, defaults to -1", + "function M.clear_extmarks(start_line, end_line)", + " if not M.mounted() or not state.windows.output_buf then", + " return", + " end", + "", + " start_line = start_line or 0", + " end_line = end_line or -1", + "", + " vim.api.nvim_buf_clear_namespace(state.windows.output_buf, M.namespace, start_line, end_line)", + "end", + "", + "---Apply extmarks to the output buffer", + "---@param extmarks table Extmarks indexed by line", + "---@param line_offset? integer Line offset to apply to extmarks, defaults to 0", + "function M.set_extmarks(extmarks, line_offset)", + " if not M.mounted() or not extmarks or type(extmarks) ~= 'table' then", + " return", + " end", + "", + " line_offset = line_offset or 0", + "", + " local output_buf = state.windows.output_buf", + "", + " for line_idx, marks in pairs(extmarks) do", + " for _, mark in ipairs(marks) do", + " local actual_mark = type(mark) == 'function' and mark() or mark", + " local target_line = line_offset + line_idx", + " if actual_mark.end_row then", + " actual_mark.end_row = actual_mark.end_row + line_offset", + " end", + " local start_col = actual_mark.start_col", + " if actual_mark.start_col then", + " actual_mark.start_col = nil", + " end", + " pcall(vim.api.nvim_buf_set_extmark, output_buf, M.namespace, target_line, start_col or 0, actual_mark)", + " end", + " end", + "end", + "", + "function M.focus_output(should_stop_insert)", + " if should_stop_insert then", + " vim.cmd('stopinsert')", + " end", + " vim.api.nvim_set_current_win(state.windows.output_win)", + "end", + "", + "function M.close()", + " if M.mounted() then", + " return", + " end", + " pcall(vim.api.nvim_win_close, state.windows.output_win, true)", + " pcall(vim.api.nvim_buf_delete, state.windows.output_buf, { force = true })", + "end", + "", + "function M.setup_keymaps(windows)", + " local keymap = require('opencode.keymap')", + " keymap.setup_window_keymaps(config.keymap.output_window, windows.output_buf)", + "end", + "", + "function M.setup_autocmds(windows, group)", + " vim.api.nvim_create_autocmd('WinEnter', {", + " group = group,", + " buffer = windows.output_buf,", + " callback = function()", + " vim.cmd('stopinsert')", + " state.last_focused_opencode_window = 'output'", + " require('opencode.ui.input_window').refresh_placeholder(state.windows)", + " end,", + " })", + "", + " vim.api.nvim_create_autocmd('BufEnter', {", + " group = group,", + " buffer = windows.output_buf,", + " callback = function()", + " vim.cmd('stopinsert')", + " state.last_focused_opencode_window = 'output'", + " require('opencode.ui.input_window').refresh_placeholder(state.windows)", + " end,", + " })", + "", + " state.subscribe('current_permission', function()", + " require('opencode.keymap').toggle_permission_keymap(windows.output_buf)", + " end)", + "end", + "", + "function M.clear()", + " M.set_lines({})", + " M.clear_extmarks()", + "end", + "", + "return M", + "```", + "", + "**Shell / Commands**", + "", + "```bash", + "# show project root and run tests", + "pwd", + "ls -la", + " ./run_tests.sh", + "```", + "", + "**Short Lua examples**", + "", + "```lua", + "-- Print buffer line count if mounted", + "local out = require('opencode.ui.output_window')", + "if out.mounted() then", + " print('Lines:', out.get_buf_line_count())", + "else", + " print('Output window not mounted.')", + "end", + "```", + "", + "**JSON sample**", + "", + "```json", + "{", + " \"name\": \"opencode.nvim\",", + " \"version\": \"0.0.0\",", + " \"tests\": {", + " \"unit\": 128,", + " \"manual\": 12", + " }", + "}", + "```", + "", + "**YAML sample**", + "", + "```yaml", + "ui:", + " window_width: 0.5", + " window_highlight: \"Normal:NormalFloat\"", + "keymap:", + " output_window:", + " close: \"\"", + " focus: \"\"", + "```", + "", + "**Unified Diff example**", + "", + "```diff", + "*** before/lua/opencode/ui/output_window.lua", + "@@", + "- vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight, { win = windows.output_win })", + "+ vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight or '', { win = windows.output_win })", + "```", + "", + "**Python snippet**", + "", + "```python", + "# small helper to format lines", + "def format_lines(lines):", + " return [line.rstrip() for line in lines if line is not None]", + "```", + "", + "**SQL example**", + "", + "```sql", + "-- List recent sessions", + "SELECT id, created_at, user_id", + "FROM sessions", + "WHERE created_at > now() - interval '7 days'", + "ORDER BY created_at DESC", + "LIMIT 10;", + "```", + "", + "**HTML fragment**", + "", + "```html", + "", + "", + " Render Test", + " ", + "
-- sample Lua inside HTML
", + " ", + "", + "```", + "", + "**Indented (classic) code block**", + "", + " This is an indented code block.", + " It should render as preformatted text without language highlighting.", + "", + "**Shell snippet with inline env vars**", + "", + "```bash", + "export OPENCODE_ENV=development", + "nvim --headless -u  tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = ' ./tests/minimal/init.lua'})\"", + "```", + "", + "**Mixed inline code examples**", + "- Use backticks for commands:  `./run_tests.sh`", + "- File path with start line:  `lua/opencode/ui/output_window.lua:1`", + "- API call: `vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)`", + "", + "**Small checklist**", + "- [x] Lua file included ( `lua/opencode/ui/output_window.lua:1`)", + "- [x] Multiple fenced code blocks", + "- [x] Several languages: Lua, Bash, JSON, YAML, Python, SQL, HTML, Diff", + "", + "If you want more variations (many short blocks, nested lists, tables, or extremely long blocks), tell me which formats or languages to expand and I’ll produce them.", + "", + "" + ], + "timestamp": 1769091196 +} diff --git a/tests/data/permission-ask-new-approve.expected.json b/tests/data/permission-ask-new-approve.expected.json index 4340e8d2..49a56564 100644 --- a/tests/data/permission-ask-new-approve.expected.json +++ b/tests/data/permission-ask-new-approve.expected.json @@ -1 +1,604 @@ -{"actions":[{"args":["5ba6e95774829e7f501299039b72b72dc32c6620"],"type":"diff_revert_selected_file","display_line":36,"text":"[R]evert file","key":"R","range":{"from":36,"to":36}},{"args":["5ba6e95774829e7f501299039b72b72dc32c6620"],"type":"diff_revert_all","display_line":36,"text":"Revert [A]ll","key":"A","range":{"from":36,"to":36}},{"args":["5ba6e95774829e7f501299039b72b72dc32c6620"],"type":"diff_open","display_line":36,"text":"[D]iff","key":"D","range":{"from":36,"to":36}}],"extmarks":[[1,1,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-05 14:07:54)","OpencodeHint"],[" [msg_b8e7c60a2001Kisjwk2mVB4dye]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}],[2,2,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[3,3,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[4,4,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[5,5,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[6,6,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[7,7,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[8,8,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[9,10,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-05 14:07:54)","OpencodeHint"],[" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}],[10,12,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[11,13,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[12,14,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[13,15,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[14,16,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[15,17,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[16,18,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[17,19,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[18,20,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[19,21,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[20,22,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[21,23,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[22,24,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[23,25,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[24,26,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[25,27,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[26,28,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[27,29,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[28,30,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[29,31,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[30,32,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[31,33,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[32,34,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[33,39,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-05 14:08:26)","OpencodeHint"],[" [msg_b8e7cde9e0013hRkUyt0X2yMil]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}]],"timestamp":1769091210,"lines":["----","","","@no-trust run a git status and tell me what files are changed","","[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)","","**Diagnostics:**  (11)","","----","","","** run** `Shows working tree status`","","`````bash","> git status","","On branch main","Your branch is up to date with 'origin/main'.","","Changes not staged for commit:"," (use \"git add ...\" to update what will be committed)"," (use \"git restore ...\" to discard changes in working directory)","\tmodified: lua/opencode/event_manager.lua","\tmodified: lua/opencode/ui/formatter.lua","\tmodified: lua/opencode/ui/renderer.lua","","Untracked files:"," (use \"git add ...\" to include in what will be committed)","\ttest.lua","\ttests/data/permission_ask_new.json","","no changes added to commit (use \"git add\" and/or \"git commit -a\")","","`````","","**󰻛 Created Snapshot** `5ba6e957`","","----","","","Here are the files that have been changed according to git status:","","- Modified (but not staged):"," -  `lua/opencode/event_manager.lua`"," -  `lua/opencode/ui/formatter.lua`"," -  `lua/opencode/ui/renderer.lua`","","- Untracked files:"," - `test.lua`"," - `tests/data/permission_ask_new.json`","","No files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!","",""]} +{ + "actions": [ + { + "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], + "type": "diff_revert_selected_file", + "display_line": 36, + "text": "[R]evert file", + "key": "R", + "range": { "from": 36, "to": 36 } + }, + { + "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], + "type": "diff_revert_all", + "display_line": 36, + "text": "Revert [A]ll", + "key": "A", + "range": { "from": 36, "to": 36 } + }, + { + "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], + "type": "diff_open", + "display_line": 36, + "text": "[D]iff", + "key": "D", + "range": { "from": 36, "to": 36 } + } + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-05 14:07:54)", "OpencodeHint"], + [" [msg_b8e7c60a2001Kisjwk2mVB4dye]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 4, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 5, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 6, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 7, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 8, + 8, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 10, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-05 14:07:54)", "OpencodeHint"], + [" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ], + [ + 10, + 12, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 11, + 13, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 12, + 14, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 13, + 15, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 14, + 16, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 15, + 17, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 16, + 18, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 17, + 19, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 18, + 20, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 19, + 21, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 20, + 22, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 21, + 23, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 22, + 24, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 23, + 25, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 24, + 26, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 25, + 27, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 26, + 28, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 27, + 29, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 28, + 30, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 29, + 31, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 30, + 32, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 31, + 33, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 32, + 34, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 33, + 39, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-05 14:08:26)", "OpencodeHint"], + [" [msg_b8e7cde9e0013hRkUyt0X2yMil]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ] + ], + "timestamp": 1769091210, + "lines": [ + "----", + "", + "", + "@no-trust run a git status and tell me what files are changed", + "", + "[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)", + "", + "**Diagnostics:**  (11)", + "", + "----", + "", + "", + "** run** `Shows working tree status`", + "", + "`````bash", + "> git status", + "", + "On branch main", + "Your branch is up to date with 'origin/main'.", + "", + "Changes not staged for commit:", + " (use \"git add ...\" to update what will be committed)", + " (use \"git restore ...\" to discard changes in working directory)", + "\tmodified: lua/opencode/event_manager.lua", + "\tmodified: lua/opencode/ui/formatter.lua", + "\tmodified: lua/opencode/ui/renderer.lua", + "", + "Untracked files:", + " (use \"git add ...\" to include in what will be committed)", + "\ttest.lua", + "\ttests/data/permission_ask_new.json", + "", + "no changes added to commit (use \"git add\" and/or \"git commit -a\")", + "", + "`````", + "", + "**󰻛 Created Snapshot** `5ba6e957`", + "", + "----", + "", + "", + "Here are the files that have been changed according to git status:", + "", + "- Modified (but not staged):", + " -  `lua/opencode/event_manager.lua`", + " -  `lua/opencode/ui/formatter.lua`", + " -  `lua/opencode/ui/renderer.lua`", + "", + "- Untracked files:", + " - `test.lua`", + " - `tests/data/permission_ask_new.json`", + "", + "No files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!", + "", + "" + ] +} diff --git a/tests/data/permission-ask-new-approve.json b/tests/data/permission-ask-new-approve.json index d6849d60..c05dcfba 100644 --- a/tests/data/permission-ask-new-approve.json +++ b/tests/data/permission-ask-new-approve.json @@ -1 +1,793 @@ -[{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"updated":1767622072149,"created":1767622072149}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","time":{"created":1767622074530}}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3001M2zsH9REfPdlQt","text":"@no-trust run a git status and tell me what files are changed","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3003Ta9NcXkN0lb0lF","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a7001d3WhKOu2vCR4Cf","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua","id":"prt_b8e7c60a7002ybV09VDHoQQeO7","filename":"lua/opencode/ui/formatter.lua","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","mime":"text/plain"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3002XHHlk77vAPApvR","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"{\"content\":[{\"msg\":\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l490:c43\"},{\"msg\":\"expected `integer` but found `number`.\",\"severity\":2,\"pos\":\"l492:c45\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l492:c62\"},{\"msg\":\"state.active_session may be nil\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"state.messages[#state.messages] may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"state.messages may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"Undefined field `[diag.severity]`.\",\"severity\":2,\"pos\":\"l435:c42\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l526:c34\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l527:c47\"},{\"msg\":\"Cannot assign `number` to `integer`.\",\"severity\":2,\"pos\":\"l493:c5\"}],\"context_type\":\"diagnostics\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"updated":1767622074603,"created":1767622072149}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","time":{"created":1767622074609},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":0,"reasoning":0,"input":0,"cache":{"write":0,"read":0}}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"diffs":[]}}},"type":"message.updated"},{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622074670,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":0,"files":0}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"step-start","id":"prt_b8e7c6672001F10WAiQKbzNEtE","snapshot":"5ba6e95774829e7f501299039b72b72dc32c6620","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"tool","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7c6686001cCV44vsFtbpF42","state":{"status":"pending","input":{},"raw":""},"callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx","tool":"bash"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"tool","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7c6686001cCV44vsFtbpF42","state":{"status":"running","input":{"description":"Shows working tree status","command":"git status"},"time":{"start":1767622076189}},"callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx","tool":"bash"}},"type":"message.part.updated"},{"properties":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","tool":{"messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx"},"patterns":["git status"],"id":"per_b8e7c6744001DX6EZ9KHM15RLi","permission":"bash","always":["git status*"],"metadata":{}},"type":"permission.asked"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"title":"Running git status to list changed files","diffs":[]}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"title":"Running git status for formatter.lua changes","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622082619,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":0,"files":0}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","requestID":"per_b8e7c6744001DX6EZ9KHM15RLi","reply":"once"},"type":"permission.replied"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"tool","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7c6686001cCV44vsFtbpF42","state":{"title":"Shows working tree status","input":{"description":"Shows working tree status","command":"git status"},"output":"On branch main\nYour branch is up to date with 'origin/main'.\n\nChanges not staged for commit:\n (use \"git add ...\" to update what will be committed)\n (use \"git restore ...\" to discard changes in working directory)\n\tmodified: lua/opencode/event_manager.lua\n\tmodified: lua/opencode/ui/formatter.lua\n\tmodified: lua/opencode/ui/renderer.lua\n\nUntracked files:\n (use \"git add ...\" to include in what will be committed)\n\ttest.lua\n\ttests/data/permission_ask_new.json\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n","status":"completed","metadata":{"exit":0,"description":"Shows working tree status","output":"On branch main\nYour branch is up to date with 'origin/main'.\n\nChanges not staged for commit:\n (use \"git add ...\" to update what will be committed)\n (use \"git restore ...\" to discard changes in working directory)\n\tmodified: lua/opencode/event_manager.lua\n\tmodified: lua/opencode/ui/formatter.lua\n\tmodified: lua/opencode/ui/renderer.lua\n\nUntracked files:\n (use \"git add ...\" to include in what will be committed)\n\ttest.lua\n\ttests/data/permission_ask_new.json\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n"},"time":{"start":1767622076189,"end":1767622106719}},"callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx","tool":"bash"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"step-finish","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7cde62001EgRfgQpjNPuSc8","reason":"tool-calls","cost":0,"snapshot":"34a4eef77f714f2e3a282c410dbda315c4781fee","tokens":{"output":23,"reasoning":0,"input":10887,"cache":{"write":0,"read":9984}}}},"type":"message.part.updated"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","finish":"tool-calls","time":{"created":1767622074609},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":23,"reasoning":0,"input":10887,"cache":{"write":0,"read":9984}}}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"patch","id":"prt_b8e7cde8f001JcNpZEALOVaZnq","hash":"5ba6e95774829e7f501299039b72b72dc32c6620","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","files":["/home/francis/Projects/_nvim/opencode.nvim/tests/data/permission_ask_new.json"]}},"type":"message.part.updated"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","finish":"tool-calls","time":{"created":1767622074609,"completed":1767622106776},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":23,"reasoning":0,"input":10887,"cache":{"write":0,"read":9984}}}},"type":"message.updated"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","time":{"created":1767622106782},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7cde9e0013hRkUyt0X2yMil","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":0,"reasoning":0,"input":0,"cache":{"write":0,"read":0}}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"title":"Running git status to list changed files","diffs":[{"file":"tests/data/permission_ask_new.json","after":"[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n","before":"","deletions":0,"additions":1}]}}},"type":"message.updated"},{"properties":{"info":{"title":"Running git status for formatter.lua changes","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622106805,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":1,"files":1}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"step-start","id":"prt_b8e7ce0ca001Hb5Sp11isapDGi","snapshot":"34a4eef77f714f2e3a282c410dbda315c4781fee","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil"}},"type":"message.part.updated"},{"properties":{"delta":" the","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" according","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" staged","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":".lua","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":"formatter","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":"/","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" -","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n -","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":"/","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" currently","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" want","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":" stage","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"delta":"!","part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!","time":{"start":1767622107340}}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce0cc001nZUgxwsgHwogal","text":"Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!","time":{"start":1767622108550,"end":1767622108550}}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"step-finish","messageID":"msg_b8e7cde9e0013hRkUyt0X2yMil","id":"prt_b8e7ce5860011MSYAyLSbWKBop","reason":"stop","cost":0,"snapshot":"34a4eef77f714f2e3a282c410dbda315c4781fee","tokens":{"output":111,"reasoning":0,"input":179,"cache":{"write":0,"read":20864}}}},"type":"message.part.updated"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","finish":"stop","time":{"created":1767622106782},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7cde9e0013hRkUyt0X2yMil","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":111,"reasoning":0,"input":179,"cache":{"write":0,"read":20864}}}},"type":"message.updated"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","finish":"stop","time":{"created":1767622106782,"completed":1767622108574},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7cde9e0013hRkUyt0X2yMil","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":111,"reasoning":0,"input":179,"cache":{"write":0,"read":20864}}}},"type":"message.updated"},{"properties":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8"},"type":"session.idle"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"title":"Running git status to list changed files","diffs":[{"file":"tests/data/permission_ask_new.json","after":"[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n","before":"","deletions":0,"additions":1}]}}},"type":"message.updated"},{"properties":{"info":{"title":"Running git status for formatter.lua changes","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622108600,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":1,"files":1}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"title":"Running git status to list changed files","diffs":[{"file":"tests/data/permission_ask_new.json","after":"[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n","before":"","deletions":0,"additions":1}],"body":"I updated the UI and event handling code by modifying `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua`, `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua`, and `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua` to improve formatting and rendering behavior. I also added `file:///home/francis/Projects/_nvim/opencode.nvim/test.lua` and `file:///home/francis/Projects/_nvim/opencode.nvim/tests/data/permission_ask_new.json` as new test/data files."}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"}] +[ + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "updated": 1767622072149, "created": 1767622072149 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "time": { "created": 1767622074530 } + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3001M2zsH9REfPdlQt", + "text": "@no-trust run a git status and tell me what files are changed", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3003Ta9NcXkN0lb0lF", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a7001d3WhKOu2vCR4Cf", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua", + "id": "prt_b8e7c60a7002ybV09VDHoQQeO7", + "filename": "lua/opencode/ui/formatter.lua", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "mime": "text/plain" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3002XHHlk77vAPApvR", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "{\"content\":[{\"msg\":\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l490:c43\"},{\"msg\":\"expected `integer` but found `number`.\",\"severity\":2,\"pos\":\"l492:c45\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l492:c62\"},{\"msg\":\"state.active_session may be nil\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"state.messages[#state.messages] may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"state.messages may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"Undefined field `[diag.severity]`.\",\"severity\":2,\"pos\":\"l435:c42\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l526:c34\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l527:c47\"},{\"msg\":\"Cannot assign `number` to `integer`.\",\"severity\":2,\"pos\":\"l493:c5\"}],\"context_type\":\"diagnostics\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "updated": 1767622074603, "created": 1767622072149 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "time": { "created": 1767622074609 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 0, "reasoning": 0, "input": 0, "cache": { "write": 0, "read": 0 } } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { "diffs": [] } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622074670, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "step-start", + "id": "prt_b8e7c6672001F10WAiQKbzNEtE", + "snapshot": "5ba6e95774829e7f501299039b72b72dc32c6620", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "tool", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7c6686001cCV44vsFtbpF42", + "state": { "status": "pending", "input": {}, "raw": "" }, + "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx", + "tool": "bash" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "tool", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7c6686001cCV44vsFtbpF42", + "state": { + "status": "running", + "input": { "description": "Shows working tree status", "command": "git status" }, + "time": { "start": 1767622076189 } + }, + "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx", + "tool": "bash" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "tool": { "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx" }, + "patterns": ["git status"], + "id": "per_b8e7c6744001DX6EZ9KHM15RLi", + "permission": "bash", + "always": ["git status*"], + "metadata": {} + }, + "type": "permission.asked" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { "title": "Running git status to list changed files", "diffs": [] } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "title": "Running git status for formatter.lua changes", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622082619, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "requestID": "per_b8e7c6744001DX6EZ9KHM15RLi", + "reply": "once" + }, + "type": "permission.replied" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "tool", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7c6686001cCV44vsFtbpF42", + "state": { + "title": "Shows working tree status", + "input": { "description": "Shows working tree status", "command": "git status" }, + "output": "On branch main\nYour branch is up to date with 'origin/main'.\n\nChanges not staged for commit:\n (use \"git add ...\" to update what will be committed)\n (use \"git restore ...\" to discard changes in working directory)\n\tmodified: lua/opencode/event_manager.lua\n\tmodified: lua/opencode/ui/formatter.lua\n\tmodified: lua/opencode/ui/renderer.lua\n\nUntracked files:\n (use \"git add ...\" to include in what will be committed)\n\ttest.lua\n\ttests/data/permission_ask_new.json\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n", + "status": "completed", + "metadata": { + "exit": 0, + "description": "Shows working tree status", + "output": "On branch main\nYour branch is up to date with 'origin/main'.\n\nChanges not staged for commit:\n (use \"git add ...\" to update what will be committed)\n (use \"git restore ...\" to discard changes in working directory)\n\tmodified: lua/opencode/event_manager.lua\n\tmodified: lua/opencode/ui/formatter.lua\n\tmodified: lua/opencode/ui/renderer.lua\n\nUntracked files:\n (use \"git add ...\" to include in what will be committed)\n\ttest.lua\n\ttests/data/permission_ask_new.json\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n" + }, + "time": { "start": 1767622076189, "end": 1767622106719 } + }, + "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx", + "tool": "bash" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "step-finish", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7cde62001EgRfgQpjNPuSc8", + "reason": "tool-calls", + "cost": 0, + "snapshot": "34a4eef77f714f2e3a282c410dbda315c4781fee", + "tokens": { "output": 23, "reasoning": 0, "input": 10887, "cache": { "write": 0, "read": 9984 } } + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "finish": "tool-calls", + "time": { "created": 1767622074609 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 23, "reasoning": 0, "input": 10887, "cache": { "write": 0, "read": 9984 } } + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "patch", + "id": "prt_b8e7cde8f001JcNpZEALOVaZnq", + "hash": "5ba6e95774829e7f501299039b72b72dc32c6620", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/tests/data/permission_ask_new.json"] + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "finish": "tool-calls", + "time": { "created": 1767622074609, "completed": 1767622106776 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 23, "reasoning": 0, "input": 10887, "cache": { "write": 0, "read": 9984 } } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "time": { "created": 1767622106782 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 0, "reasoning": 0, "input": 0, "cache": { "write": 0, "read": 0 } } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { + "title": "Running git status to list changed files", + "diffs": [ + { + "file": "tests/data/permission_ask_new.json", + "after": "[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n", + "before": "", + "deletions": 0, + "additions": 1 + } + ] + } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "title": "Running git status for formatter.lua changes", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622106805, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 1, "files": 1 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "step-start", + "id": "prt_b8e7ce0ca001Hb5Sp11isapDGi", + "snapshot": "34a4eef77f714f2e3a282c410dbda315c4781fee", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "delta": " the", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " according", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " staged", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": ".lua", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": "formatter", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": "/", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " -", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n -", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": "/", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " currently", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " want", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": " stage", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "delta": "!", + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!", + "time": { "start": 1767622107340 } + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce0cc001nZUgxwsgHwogal", + "text": "Here are the files that have been changed according to git status:\n\n- Modified (but not staged):\n - `lua/opencode/event_manager.lua`\n - `lua/opencode/ui/formatter.lua`\n - `lua/opencode/ui/renderer.lua`\n\n- Untracked files:\n - `test.lua`\n - `tests/data/permission_ask_new.json`\n\nNo files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!", + "time": { "start": 1767622108550, "end": 1767622108550 } + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "step-finish", + "messageID": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "id": "prt_b8e7ce5860011MSYAyLSbWKBop", + "reason": "stop", + "cost": 0, + "snapshot": "34a4eef77f714f2e3a282c410dbda315c4781fee", + "tokens": { "output": 111, "reasoning": 0, "input": 179, "cache": { "write": 0, "read": 20864 } } + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "finish": "stop", + "time": { "created": 1767622106782 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 111, "reasoning": 0, "input": 179, "cache": { "write": 0, "read": 20864 } } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "finish": "stop", + "time": { "created": 1767622106782, "completed": 1767622108574 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7cde9e0013hRkUyt0X2yMil", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 111, "reasoning": 0, "input": 179, "cache": { "write": 0, "read": 20864 } } + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8" }, "type": "session.idle" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { + "title": "Running git status to list changed files", + "diffs": [ + { + "file": "tests/data/permission_ask_new.json", + "after": "[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n", + "before": "", + "deletions": 0, + "additions": 1 + } + ] + } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "title": "Running git status for formatter.lua changes", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622108600, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 1, "files": 1 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { + "title": "Running git status to list changed files", + "diffs": [ + { + "file": "tests/data/permission_ask_new.json", + "after": "[{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622072149,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"time\":{\"created\":1767622074530}}},\"type\":\"message.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3001M2zsH9REfPdlQt\",\"text\":\"@no-trust run a git status and tell me what files are changed\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3003Ta9NcXkN0lb0lF\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"Called the Read tool with the following input: {\\\"filePath\\\":\\\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a7001d3WhKOu2vCR4Cf\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"\\n00001| local context_module = require('opencode.context')\\n00002| local icons = require('opencode.ui.icons')\\n00003| local util = require('opencode.util')\\n00004| local Output = require('opencode.ui.output')\\n00005| local state = require('opencode.state')\\n00006| local config = require('opencode.config')\\n00007| local snapshot = require('opencode.snapshot')\\n00008| local mention = require('opencode.ui.mention')\\n00009| \\n00010| local M = {}\\n00011| \\n00012| M.separator = {\\n00013| '----',\\n00014| '',\\n00015| }\\n00016| \\n00017| ---@param output Output\\n00018| ---@param part OpencodeMessagePart\\n00019| function M._format_reasoning(output, part)\\n00020| local text = vim.trim(part.text or '')\\n00021| if text == '' then\\n00022| return\\n00023| end\\n00024| \\n00025| local start_line = output:get_line_count() + 1\\n00026| \\n00027| local title = 'Reasoning'\\n00028| local time = part.time\\n00029| if time and type(time) == 'table' and time.start then\\n00030| local start_text = util.format_time(time.start) or ''\\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\\n00032| if end_text and end_text ~= '' then\\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\\n00034| elseif start_text ~= '' then\\n00035| title = string.format('%s (%s)', title, start_text)\\n00036| end\\n00037| end\\n00038| \\n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\\n00040| \\n00041| if config.ui.output.tools.show_reasoning_output then\\n00042| output:add_empty_line()\\n00043| output:add_lines(vim.split(text, '\\\\n'))\\n00044| output:add_empty_line()\\n00045| end\\n00046| \\n00047| local end_line = output:get_line_count()\\n00048| if end_line - start_line > 1 then\\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\\n00050| else\\n00051| output:add_extmark(start_line - 1, {\\n00052| line_hl_group = 'OpencodeReasoningText',\\n00053| } --[[@as OutputExtmark]])\\n00054| end\\n00055| end\\n00056| \\n00057| function M._handle_permission_request(output, part)\\n00058| if part.state and part.state.status == 'error' and part.state.error then\\n00059| if part.state.error:match('rejected permission') then\\n00060| state.current_permission = nil\\n00061| else\\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\\n00063| end\\n00064| return\\n00065| end\\n00066| \\n00067| M._format_permission_request(output)\\n00068| end\\n00069| \\n00070| function M._format_permission_request(output)\\n00071| local keys\\n00072| \\n00073| if require('opencode.ui.ui').is_opencode_focused() then\\n00074| keys = {\\n00075| config.keymap.permission.accept,\\n00076| config.keymap.permission.accept_all,\\n00077| config.keymap.permission.deny,\\n00078| }\\n00079| else\\n00080| keys = {\\n00081| config.get_key_for_function('editor', 'permission_accept'),\\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\\n00083| config.get_key_for_function('editor', 'permission_deny'),\\n00084| }\\n00085| end\\n00086| \\n00087| output:add_empty_line()\\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\\n00089| output:add_line('>')\\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\\n00091| output:add_empty_line()\\n00092| end\\n00093| \\n00094| ---Calculate statistics for reverted messages and tool calls\\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\\n00096| ---@param revert_index number Index of the message where revert occurred\\n00097| ---@param revert_info SessionRevertInfo Revert information\\n00098| ---@return {messages: number, tool_calls: number, files: table}\\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\\n00100| local stats = {\\n00101| messages = 0,\\n00102| tool_calls = 0,\\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\\n00104| }\\n00105| \\n00106| for i = revert_index, #messages do\\n00107| local msg = messages[i]\\n00108| if msg.info.role == 'user' then\\n00109| stats.messages = stats.messages + 1\\n00110| end\\n00111| if msg.parts then\\n00112| for _, part in ipairs(msg.parts) do\\n00113| if part.type == 'tool' then\\n00114| stats.tool_calls = stats.tool_calls + 1\\n00115| end\\n00116| end\\n00117| end\\n00118| end\\n00119| \\n00120| if revert_info.diff then\\n00121| local current_file = nil\\n00122| for line in revert_info.diff:gmatch('[^\\\\r\\\\n]+') do\\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\\n00125| if file_b then\\n00126| current_file = file_b:gsub('^[ab]/', '')\\n00127| if not stats.files[current_file] then\\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00129| end\\n00130| elseif file_a then\\n00131| current_file = file_a:gsub('^[ab]/', '')\\n00132| if not stats.files[current_file] then\\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\\n00134| end\\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\\n00136| if current_file then\\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\\n00138| end\\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\\n00140| if current_file then\\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\\n00142| end\\n00143| end\\n00144| end\\n00145| end\\n00146| \\n00147| return stats\\n00148| end\\n00149| \\n00150| ---Format the revert callout with statistics\\n00151| ---@param session_data OpencodeMessage[] All messages in the session\\n00152| ---@param start_idx number Index of the message where revert occurred\\n00153| ---@return Output output object representing the lines, extmarks, and actions\\n00154| function M._format_revert_message(session_data, start_idx)\\n00155| local output = Output.new()\\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\\n00159| \\n00160| output:add_lines(M.separator)\\n00161| output:add_line(\\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\\n00163| )\\n00164| output:add_line('>')\\n00165| output:add_line('> type `/redo` to restore.')\\n00166| output:add_empty_line()\\n00167| \\n00168| if stats.files and next(stats.files) then\\n00169| for file, fstats in pairs(stats.files) do\\n00170| local file_diff = {}\\n00171| if fstats.additions > 0 then\\n00172| table.insert(file_diff, '+' .. fstats.additions)\\n00173| end\\n00174| if fstats.deletions > 0 then\\n00175| table.insert(file_diff, '-' .. fstats.deletions)\\n00176| end\\n00177| if #file_diff > 0 then\\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\\n00179| local line_idx = output:add_line(line_str)\\n00180| local col = #(' ' .. file .. ': ')\\n00181| for _, diff in ipairs(file_diff) do\\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\\n00183| output:add_extmark(line_idx - 1, {\\n00184| virt_text = { { diff, hl_group } },\\n00185| virt_text_pos = 'inline',\\n00186| virt_text_win_col = col,\\n00187| priority = 1000,\\n00188| } --[[@as OutputExtmark]])\\n00189| col = col + #diff + 1\\n00190| end\\n00191| end\\n00192| end\\n00193| end\\n00194| return output\\n00195| end\\n00196| \\n00197| local function add_action(output, text, action_type, args, key, line)\\n00198| -- actions use api-indexing (e.g. 0 indexed)\\n00199| line = (line or output:get_line_count()) - 1\\n00200| output:add_action({\\n00201| text = text,\\n00202| type = action_type,\\n00203| args = args,\\n00204| key = key,\\n00205| display_line = line,\\n00206| range = { from = line, to = line },\\n00207| })\\n00208| end\\n00209| \\n00210| ---@param output Output Output object to write to\\n00211| ---@param part OpencodeMessagePart\\n00212| function M._format_patch(output, part)\\n00213| if not part.hash then\\n00214| return\\n00215| end\\n00216| \\n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\\n00219| \\n00220| -- Anchor all snapshot-level actions to the snapshot header line\\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\\n00224| \\n00225| if #restore_points > 0 then\\n00226| for _, restore_point in ipairs(restore_points) do\\n00227| output:add_line(\\n00228| string.format(\\n00229| ' %s Restore point `%s` - %s ',\\n00230| icons.get('restore_point'),\\n00231| vim.trim(restore_point.id:sub(1, 8)),\\n00232| util.format_time(restore_point.created_at)\\n00233| )\\n00234| )\\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\\n00237| end\\n00238| end\\n00239| end\\n00240| \\n00241| ---@param output Output Output object to write to\\n00242| ---@param message MessageInfo\\n00243| function M._format_error(output, message)\\n00244| output:add_empty_line()\\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\\n00246| end\\n00247| \\n00248| ---@param message OpencodeMessage\\n00249| ---@return Output\\n00250| function M.format_message_header(message)\\n00251| local output = Output.new()\\n00252| \\n00253| output:add_lines(M.separator)\\n00254| local role = message.info.role or 'unknown'\\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\\n00256| \\n00257| local time = message.info.time and message.info.time.created or nil\\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\\n00261| \\n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\\n00263| \\n00264| local display_name\\n00265| if role == 'assistant' then\\n00266| local mode = message.info.mode\\n00267| if mode and mode ~= '' then\\n00268| display_name = mode:upper()\\n00269| else\\n00270| -- For the most recent assistant message, show current_mode if mode is missing\\n00271| -- This handles new messages that haven't been stamped yet\\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\\n00274| display_name = state.current_mode:upper()\\n00275| else\\n00276| display_name = 'ASSISTANT'\\n00277| end\\n00278| end\\n00279| else\\n00280| display_name = role:upper()\\n00281| end\\n00282| \\n00283| output:add_extmark(output:get_line_count() - 1, {\\n00284| virt_text = {\\n00285| { icon, role_hl },\\n00286| { ' ' },\\n00287| { display_name, role_hl },\\n00288| { model_text, 'OpencodeHint' },\\n00289| { time_text, 'OpencodeHint' },\\n00290| { debug_text, 'OpencodeHint' },\\n00291| },\\n00292| virt_text_win_col = -3,\\n00293| priority = 10,\\n00294| } --[[@as OutputExtmark]])\\n00295| \\n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\\n00297| -- handle rendering the error\\n00298| if\\n00299| role == 'assistant'\\n00300| and message.info.error\\n00301| and message.info.error ~= ''\\n00302| and (not message.parts or #message.parts == 0)\\n00303| then\\n00304| local error = message.info.error\\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\\n00306| \\n00307| output:add_line('')\\n00308| M._format_callout(output, 'ERROR', error_message)\\n00309| end\\n00310| \\n00311| output:add_line('')\\n00312| return output\\n00313| end\\n00314| \\n00315| ---@param output Output Output object to write to\\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\\n00317| ---@param text string Callout text content\\n00318| ---@param title? string Optional title for the callout\\n00319| function M._format_callout(output, callout, text, title)\\n00320| title = title and title .. ' ' or ''\\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\\n00323| or config.ui.window_width\\n00324| or 80\\n00325| if #text > win_width - 4 then\\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\\\v(.{' .. (win_width - 8) .. '})', '\\\\1\\\\n', 'g')\\n00327| text = ok and substituted or text\\n00328| end\\n00329| \\n00330| -- Trim off any trailing newlines so there isn't an extra line in the\\n00331| -- extmarks section\\n00332| local lines = vim.split(text:gsub('\\\\n$', ''), '\\\\n')\\n00333| if #lines == 1 and title == '' then\\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\\n00335| else\\n00336| output:add_line('> [!' .. callout .. ']' .. title)\\n00337| output:add_line('>')\\n00338| output:add_lines(lines, '> ')\\n00339| end\\n00340| end\\n00341| \\n00342| ---@param output Output Output object to write to\\n00343| ---@param text string\\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\\n00345| function M._format_user_prompt(output, text, message)\\n00346| local start_line = output:get_line_count()\\n00347| \\n00348| output:add_lines(vim.split(text, '\\\\n'))\\n00349| \\n00350| local end_line = output:get_line_count()\\n00351| \\n00352| local end_line_extmark_offset = 0\\n00353| \\n00354| local mentions = {}\\n00355| if message and message.parts then\\n00356| -- message.parts will only be filled out on a re-render\\n00357| -- we need to collect the mentions here\\n00358| for _, part in ipairs(message.parts) do\\n00359| if part.type == 'file' then\\n00360| -- we're rerendering this part and we have files, the space after the user prompt\\n00361| -- also needs an extmark\\n00362| end_line_extmark_offset = 1\\n00363| if part.source and part.source.text then\\n00364| table.insert(mentions, part.source.text)\\n00365| end\\n00366| elseif part.type == 'agent' then\\n00367| if part.source then\\n00368| table.insert(mentions, part.source)\\n00369| end\\n00370| end\\n00371| end\\n00372| end\\n00373| \\n00374| if #mentions > 0 then\\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\\n00376| end\\n00377| \\n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\\n00379| end\\n00380| \\n00381| ---@param output Output Output object to write to\\n00382| ---@param part OpencodeMessagePart\\n00383| function M._format_selection_context(output, part)\\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\\n00385| if not json then\\n00386| return\\n00387| end\\n00388| local start_line = output:get_line_count()\\n00389| output:add_lines(vim.split(json.content or '', '\\\\n'))\\n00390| output:add_empty_line()\\n00391| \\n00392| local end_line = output:get_line_count()\\n00393| \\n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00395| end\\n00396| \\n00397| ---@param output Output Output object to write to\\n00398| ---@param part OpencodeMessagePart\\n00399| function M._format_cursor_data_context(output, part)\\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\\n00401| if not json then\\n00402| return\\n00403| end\\n00404| local start_line = output:get_line_count()\\n00405| output:add_line('Line ' .. json.line .. ':')\\n00406| output:add_lines(vim.split(json.line_content or '', '\\\\n'))\\n00407| output:add_empty_line()\\n00408| \\n00409| local end_line = output:get_line_count()\\n00410| \\n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00412| end\\n00413| \\n00414| ---@param output Output Output object to write to\\n00415| ---@param part OpencodeMessagePart\\n00416| function M._format_diagnostics_context(output, part)\\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\\n00418| if not json then\\n00419| return\\n00420| end\\n00421| local start_line = output:get_line_count()\\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\\n00424| return\\n00425| end\\n00426| \\n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\\n00428| local diagnostics_icons = {\\n00429| error = icons.get('error'),\\n00430| warn = icons.get('warning'),\\n00431| info = icons.get('info'),\\n00432| }\\n00433| \\n00434| for _, diag in ipairs(diagnostics) do\\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\\n00437| end\\n00438| \\n00439| local diag_line = '**Diagnostics:**'\\n00440| for name, count in pairs(diagnostics_count) do\\n00441| if count > 0 then\\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\\n00443| end\\n00444| end\\n00445| output:add_line(diag_line)\\n00446| output:add_empty_line()\\n00447| local end_line = output:get_line_count()\\n00448| \\n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\\n00450| end\\n00451| \\n00452| ---Format and display the file path in the context\\n00453| ---@param output Output Output object to write to\\n00454| ---@param path string|nil File path\\n00455| function M._format_context_file(output, path)\\n00456| if not path or path == '' then\\n00457| return\\n00458| end\\n00459| local cwd = vim.fn.getcwd()\\n00460| if vim.startswith(path, cwd) then\\n00461| path = path:sub(#cwd + 2)\\n00462| end\\n00463| return output:add_line(string.format('[%s](%s)', path, path))\\n00464| end\\n00465| \\n00466| ---@param output Output Output object to write to\\n00467| ---@param text string\\n00468| function M._format_assistant_message(output, text)\\n00469| local reference_picker = require('opencode.ui.reference_picker')\\n00470| local references = reference_picker.parse_references(text, '')\\n00471| \\n00472| -- If no references, just add the text as-is\\n00473| if #references == 0 then\\n00474| output:add_lines(vim.split(text, '\\\\n'))\\n00475| return\\n00476| end\\n00477| \\n00478| -- Sort references by match_start position (ascending)\\n00479| table.sort(references, function(a, b)\\n00480| return a.match_start < b.match_start\\n00481| end)\\n00482| \\n00483| -- Build a new text with icons inserted before each reference\\n00484| local result = ''\\n00485| local last_pos = 1\\n00486| local ref_icon = icons.get('reference')\\n00487| \\n00488| for _, ref in ipairs(references) do\\n00489| -- Add text before this reference\\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\\n00491| -- Add the icon and the reference\\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\\n00493| last_pos = ref.match_end + 1\\n00494| end\\n00495| \\n00496| -- Add any remaining text after the last reference\\n00497| if last_pos <= #text then\\n00498| result = result .. text:sub(last_pos)\\n00499| end\\n00500| \\n00501| output:add_lines(vim.split(result, '\\\\n'))\\n00502| end\\n00503| \\n00504| ---@param output Output Output object to write to\\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\\n00506| ---@param value string Value associated with the action (e.g., filename, command)\\n00507| function M._format_action(output, tool_type, value)\\n00508| if not tool_type or not value then\\n00509| return\\n00510| end\\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\\n00512| \\n00513| output:add_line(line)\\n00514| end\\n00515| \\n00516| ---@param output Output Output object to write to\\n00517| ---@param input BashToolInput data for the tool\\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\\n00519| function M._format_bash_tool(output, input, metadata)\\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\\n00521| \\n00522| if not config.ui.output.tools.show_output then\\n00523| return\\n00524| end\\n00525| \\n00526| if metadata.output or metadata.command or input.command then\\n00527| local command = input.command or metadata.command or ''\\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\\\n' .. metadata.output) or ''\\n00529| M._format_code(output, vim.split('> ' .. command .. '\\\\n' .. command_output, '\\\\n'), 'bash')\\n00530| end\\n00531| end\\n00532| \\n00533| ---@param output Output Output object to write to\\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\\n00535| ---@param input FileToolInput data for the tool\\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\\n00537| function M._format_file_tool(output, tool_type, input, metadata)\\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\\n00541| \\n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\\n00543| \\n00544| if not config.ui.output.tools.show_output then\\n00545| return\\n00546| end\\n00547| \\n00548| if tool_type == 'edit' and metadata.diff then\\n00549| M._format_diff(output, metadata.diff, file_type)\\n00550| elseif tool_type == 'write' and input and input.content then\\n00551| M._format_code(output, vim.split(input.content, '\\\\n'), file_type)\\n00552| end\\n00553| end\\n00554| \\n00555| ---@param output Output Output object to write to\\n00556| ---@param title string\\n00557| ---@param input TodoToolInput\\n00558| function M._format_todo_tool(output, title, input)\\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\\n00560| if not config.ui.output.tools.show_output then\\n00561| return\\n00562| end\\n00563| \\n00564| local todos = input and input.todos or {}\\n00565| \\n00566| for _, item in ipairs(todos) do\\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\\n00569| end\\n00570| end\\n00571| \\n00572| ---@param output Output Output object to write to\\n00573| ---@param input GlobToolInput data for the tool\\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\\n00575| function M._format_glob_tool(output, input, metadata)\\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\\n00577| if not config.ui.output.tools.show_output then\\n00578| return\\n00579| end\\n00580| local prefix = metadata.truncated and ' more than' or ''\\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\\n00582| end\\n00583| \\n00584| ---@param output Output Output object to write to\\n00585| ---@param input GrepToolInput data for the tool\\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\\n00587| function M._format_grep_tool(output, input, metadata)\\n00588| input = input or { path = '', include = '', pattern = '' }\\n00589| \\n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\\n00591| \\n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\\n00593| if not config.ui.output.tools.show_output then\\n00594| return\\n00595| end\\n00596| local prefix = metadata.truncated and ' more than' or ''\\n00597| output:add_line(\\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\\n00599| )\\n00600| end\\n00601| \\n00602| ---@param output Output Output object to write to\\n00603| ---@param input WebFetchToolInput data for the tool\\n00604| function M._format_webfetch_tool(output, input)\\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\\n00606| end\\n00607| \\n00608| ---@param output Output Output object to write to\\n00609| ---@param input ListToolInput\\n00610| ---@param metadata ListToolMetadata\\n00611| ---@param tool_output string\\n00612| function M._format_list_tool(output, input, metadata, tool_output)\\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\\n00614| if not config.ui.output.tools.show_output then\\n00615| return\\n00616| end\\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\\\n')\\n00618| if #lines < 1 or metadata.count == 0 then\\n00619| output:add_line('No files found.')\\n00620| return\\n00621| end\\n00622| if #lines > 1 then\\n00623| output:add_line('Files:')\\n00624| for i = 2, #lines do\\n00625| local file = vim.trim(lines[i])\\n00626| if file ~= '' then\\n00627| output:add_line(' • ' .. file)\\n00628| end\\n00629| end\\n00630| end\\n00631| if metadata.truncated then\\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\\n00633| end\\n00634| end\\n00635| \\n00636| ---@param output Output Output object to write to\\n00637| ---@param part OpencodeMessagePart\\n00638| function M._format_tool(output, part)\\n00639| local tool = part.tool\\n00640| if not tool or not part.state then\\n00641| return\\n00642| end\\n00643| \\n00644| local start_line = output:get_line_count() + 1\\n00645| local input = part.state.input or {}\\n00646| local metadata = part.state.metadata or {}\\n00647| local tool_output = part.state.output or ''\\n00648| \\n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\\n00650| metadata = state.current_permission.metadata or metadata\\n00651| end\\n00652| \\n00653| if tool == 'bash' then\\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\\n00657| elseif tool == 'todowrite' then\\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\\n00659| elseif tool == 'glob' then\\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\\n00661| elseif tool == 'list' then\\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\\n00663| elseif tool == 'grep' then\\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\\n00665| elseif tool == 'webfetch' then\\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\\n00667| elseif tool == 'task' then\\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\\n00669| else\\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\\n00671| end\\n00672| \\n00673| if part.state.status == 'error' and part.state.error then\\n00674| output:add_line('')\\n00675| M._format_callout(output, 'ERROR', part.state.error)\\n00676| ---@diagnostic disable-next-line: undefined-field\\n00677| elseif part.state.input and part.state.input.error then\\n00678| output:add_line('')\\n00679| ---I'm not sure about the type with state.input.error\\n00680| ---@diagnostic disable-next-line: undefined-field\\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\\n00682| end\\n00683| \\n00684| if\\n00685| state.current_permission\\n00686| and (\\n00687| (\\n00688| state.current_permission.tool\\n00689| and state.current_permission.tool.callID == part.callID\\n00690| and state.current_permission.tool.messageID == part.messageID\\n00691| )\\n00692| ---@TODO this is for backward compatibility, remove later\\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\\n00694| )\\n00695| then\\n00696| M._handle_permission_request(output, part)\\n00697| end\\n00698| \\n00699| local end_line = output:get_line_count()\\n00700| if end_line - start_line > 1 then\\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\\n00702| end\\n00703| end\\n00704| \\n00705| ---@param output Output Output object to write to\\n00706| ---@param input TaskToolInput data for the tool\\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\\n00708| ---@param tool_output string\\n00709| function M._format_task_tool(output, input, metadata, tool_output)\\n00710| local start_line = output:get_line_count() + 1\\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\\n00712| \\n00713| if config.ui.output.tools.show_output then\\n00714| if tool_output and tool_output ~= '' then\\n00715| output:add_empty_line()\\n00716| output:add_lines(vim.split(tool_output, '\\\\n'))\\n00717| output:add_empty_line()\\n00718| end\\n00719| \\n00720| if metadata.summary and type(metadata.summary) == 'table' then\\n00721| for _, sub_part in ipairs(metadata.summary) do\\n00722| if sub_part.type == 'tool' and sub_part.tool then\\n00723| M._format_tool(output, sub_part)\\n00724| end\\n00725| end\\n00726| end\\n00727| end\\n00728| \\n00729| local end_line = output:get_line_count()\\n00730| output:add_action({\\n00731| text = '[S]elect Child Session',\\n00732| type = 'select_child_session',\\n00733| args = {},\\n00734| key = 'S',\\n00735| display_line = start_line - 1,\\n00736| range = { from = start_line, to = end_line },\\n00737| })\\n00738| end\\n00739| \\n00740| ---@param output Output Output object to write to\\n00741| ---@param lines string[]\\n00742| ---@param language string\\n00743| function M._format_code(output, lines, language)\\n00744| output:add_empty_line()\\n00745| --- NOTE: use longer code fence because lines could contain ```\\n00746| output:add_line('`````' .. (language or ''))\\n00747| output:add_lines(util.sanitize_lines(lines))\\n00748| output:add_line('`````')\\n00749| end\\n00750| \\n00751| ---@param output Output Output object to write to\\n00752| ---@param code string\\n00753| ---@param file_type string\\n00754| function M._format_diff(output, code, file_type)\\n00755| output:add_empty_line()\\n00756| \\n00757| --- NOTE: use longer code fence because code could contain ```\\n00758| output:add_line('`````' .. file_type)\\n00759| local lines = vim.split(code, '\\\\n')\\n00760| if #lines > 5 then\\n00761| lines = vim.list_slice(lines, 6)\\n00762| end\\n00763| \\n00764| for _, line in ipairs(lines) do\\n00765| local first_char = line:sub(1, 1)\\n00766| if first_char == '+' or first_char == '-' then\\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\\n00768| output:add_line(' ' .. line:sub(2))\\n00769| local line_idx = output:get_line_count()\\n00770| output:add_extmark(line_idx - 1, function()\\n00771| return {\\n00772| end_col = 0,\\n00773| end_row = line_idx,\\n00774| virt_text = { { first_char, hl_group } },\\n00775| hl_group = hl_group,\\n00776| hl_eol = true,\\n00777| priority = 5000,\\n00778| right_gravity = true,\\n00779| end_right_gravity = false,\\n00780| virt_text_hide = false,\\n00781| virt_text_pos = 'overlay',\\n00782| virt_text_repeat_linebreak = false,\\n00783| }\\n00784| end)\\n00785| else\\n00786| output:add_line(line)\\n00787| end\\n00788| end\\n00789| output:add_line('`````')\\n00790| end\\n00791| \\n00792| ---@param output Output Output object to write to\\n00793| ---@param start_line number\\n00794| ---@param end_line number\\n00795| ---@param hl_group string Highlight group for the border character\\n00796| ---@param win_col number\\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\\n00799| for line = start_line, end_line do\\n00800| local extmark_opts = {\\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\\n00802| virt_text_pos = 'overlay',\\n00803| virt_text_win_col = win_col,\\n00804| virt_text_repeat_linebreak = true,\\n00805| }\\n00806| \\n00807| -- Add line highlight if text_hl_group is provided\\n00808| if text_hl_group then\\n00809| extmark_opts.line_hl_group = text_hl_group\\n00810| end\\n00811| \\n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\\n00813| end\\n00814| end\\n00815| \\n00816| ---Formats a single message part and returns the resulting output object\\n00817| ---@param part OpencodeMessagePart The part to format\\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\\n00820| ---@return Output\\n00821| function M.format_part(part, message, is_last_part)\\n00822| local output = Output.new()\\n00823| \\n00824| if not message or not message.info or not message.info.role then\\n00825| return output\\n00826| end\\n00827| \\n00828| local content_added = false\\n00829| local role = message.info.role\\n00830| \\n00831| if role == 'user' then\\n00832| if part.type == 'text' and part.text then\\n00833| if part.synthetic == true then\\n00834| M._format_selection_context(output, part)\\n00835| M._format_cursor_data_context(output, part)\\n00836| M._format_diagnostics_context(output, part)\\n00837| else\\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\\n00839| content_added = true\\n00840| end\\n00841| elseif part.type == 'file' then\\n00842| local file_line = M._format_context_file(output, part.filename)\\n00843| if file_line then\\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\\n00845| content_added = true\\n00846| end\\n00847| end\\n00848| elseif role == 'assistant' then\\n00849| if part.type == 'text' and part.text then\\n00850| M._format_assistant_message(output, vim.trim(part.text))\\n00851| content_added = true\\n00852| elseif part.type == 'reasoning' and part.text then\\n00853| M._format_reasoning(output, part)\\n00854| content_added = true\\n00855| elseif part.type == 'tool' then\\n00856| M._format_tool(output, part)\\n00857| content_added = true\\n00858| elseif part.type == 'patch' and part.hash then\\n00859| M._format_patch(output, part)\\n00860| content_added = true\\n00861| end\\n00862| end\\n00863| \\n00864| if content_added then\\n00865| output:add_empty_line()\\n00866| end\\n00867| \\n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\\n00869| local error = message.info.error\\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\\n00871| M._format_callout(output, 'ERROR', error_message)\\n00872| output:add_empty_line()\\n00873| end\\n00874| \\n00875| return output\\n00876| end\\n00877| \\n00878| return M\\n00879| \\n\\n(End of file - total 879 lines)\\n\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"file\",\"url\":\"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\",\"id\":\"prt_b8e7c60a7002ybV09VDHoQQeO7\",\"filename\":\"lua/opencode/ui/formatter.lua\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"mime\":\"text/plain\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"text\",\"id\":\"prt_b8e7c60a3002XHHlk77vAPApvR\",\"messageID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"text\":\"{\\\"content\\\":[{\\\"msg\\\":\\\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l490:c43\\\"},{\\\"msg\\\":\\\"expected `integer` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c45\\\"},{\\\"msg\\\":\\\"expected `integer?` but found `number`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l492:c62\\\"},{\\\"msg\\\":\\\"state.active_session may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l156:c68\\\"},{\\\"msg\\\":\\\"state.messages[#state.messages] may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"state.messages may be nil\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l272:c74\\\"},{\\\"msg\\\":\\\"Undefined field `[diag.severity]`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l435:c42\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l526:c34\\\"},{\\\"msg\\\":\\\"Undefined field `command`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l527:c47\\\"},{\\\"msg\\\":\\\"Cannot assign `number` to `integer`.\\\",\\\"severity\\\":2,\\\"pos\\\":\\\"l493:c5\\\"}],\\\"context_type\\\":\\\"diagnostics\\\"}\",\"synthetic\":true}},\"type\":\"message.part.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"time\":{\"updated\":1767622074603,\"created\":1767622072149}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"providerID\":\"github-copilot\",\"role\":\"assistant\",\"path\":{\"root\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"cwd\":\"/home/francis/Projects/_nvim/opencode.nvim\"},\"agent\":\"build\",\"time\":{\"created\":1767622074609},\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"mode\":\"build\",\"modelID\":\"gpt-4.1\",\"id\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"parentID\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"cost\":0,\"tokens\":{\"output\":0,\"reasoning\":0,\"input\":0,\"cache\":{\"write\":0,\"read\":0}}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":{\"info\":{\"title\":\"New session - 2026-01-05T14:07:52.149Z\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622074670,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"step-start\",\"id\":\"prt_b8e7c6672001F10WAiQKbzNEtE\",\"snapshot\":\"5ba6e95774829e7f501299039b72b72dc32c6620\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"pending\",\"input\":{},\"raw\":\"\"},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"part\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"type\":\"tool\",\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"id\":\"prt_b8e7c6686001cCV44vsFtbpF42\",\"state\":{\"status\":\"running\",\"input\":{\"description\":\"Shows working tree status\",\"command\":\"git status\"},\"time\":{\"start\":1767622076189}},\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\",\"tool\":\"bash\"}},\"type\":\"message.part.updated\"},{\"properties\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"tool\":{\"messageID\":\"msg_b8e7c60f1001aEWYlAaDRXQ4aJ\",\"callID\":\"call_KAC0LdAxKEbU6HTAPH5QJfGx\"},\"patterns\":[\"git status\"],\"id\":\"per_b8e7c6744001DX6EZ9KHM15RLi\",\"permission\":\"bash\",\"always\":[\"git status*\"],\"metadata\":{}},\"type\":\"permission.asked\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"sessionID\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"created\":1767622074530},\"model\":{\"providerID\":\"github-copilot\",\"modelID\":\"gpt-4.1\"},\"role\":\"user\",\"id\":\"msg_b8e7c60a2001Kisjwk2mVB4dye\",\"agent\":\"build\",\"system\":\"# Code References\\n\\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\\n\\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\\n\\nExamples:\\n- CORRECT: \\\"The error is in `file://src/services/process.ts:712`\\\"\\n- INCORRECT: \\\"The error is in file://src/services/process.ts:712\\\"\\n- INCORRECT: \\\"The error is in src/services/process.ts:712\\\"\\n\\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\\n\",\"summary\":{\"title\":\"Running git status to list changed files\",\"diffs\":[]}}},\"type\":\"message.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"},{\"properties\":{\"info\":{\"title\":\"Running git status for formatter.lua changes\",\"version\":\"1.1.2\",\"directory\":\"/home/francis/Projects/_nvim/opencode.nvim\",\"id\":\"ses_47183a8abffeZzOLeJ3j2KV8f8\",\"time\":{\"updated\":1767622082619,\"created\":1767622072149},\"projectID\":\"29d43526f88157cd4edd071b899dd01f240b771b\",\"summary\":{\"deletions\":0,\"additions\":0,\"files\":0}}},\"type\":\"session.updated\"},{\"properties\":[],\"type\":\"custom.emit_events.finished\"}]\n", + "before": "", + "deletions": 0, + "additions": 1 + } + ], + "body": "I updated the UI and event handling code by modifying `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/event_manager.lua`, `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua`, and `file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua` to improve formatting and rendering behavior. I also added `file:///home/francis/Projects/_nvim/opencode.nvim/test.lua` and `file:///home/francis/Projects/_nvim/opencode.nvim/tests/data/permission_ask_new.json` as new test/data files." + } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" } +] diff --git a/tests/data/permission-ask-new-deny.expected.json b/tests/data/permission-ask-new-deny.expected.json index a29f7c93..7c54fba9 100644 --- a/tests/data/permission-ask-new-deny.expected.json +++ b/tests/data/permission-ask-new-deny.expected.json @@ -1 +1,252 @@ -{"extmarks":[[1,1,0,{"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-05 14:09:08)","OpencodeHint"],[" [msg_b8e7d8222001ZAc4G1t5RfHzou]","OpencodeHint"]],"right_gravity":true,"priority":10,"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[2,2,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[3,3,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[4,4,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[5,5,0,{"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[6,8,0,{"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-05 14:09:08)","OpencodeHint"],[" [msg_b8e7d823f001HX1Rr9DYE7TYBf]","OpencodeHint"]],"right_gravity":true,"priority":10,"virt_text_repeat_linebreak":false,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-3}],[7,10,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[8,11,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[9,12,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[10,13,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[11,14,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[12,15,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[13,16,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}],[14,17,0,{"ns_id":3,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text_win_col":-1}]],"timestamp":1767622360,"lines":["----","","","@no-trust run a git status and tell me what files are changed","","[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)","","----","","","** run** `Shows working tree status`","","`````bash","> git status","","`````","","> [!ERROR] Error: The user rejected permission to use this specific tool call.","",""],"actions":[]} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-05 14:09:08)", "OpencodeHint"], + [" [msg_b8e7d8222001ZAc4G1t5RfHzou]", "OpencodeHint"] + ], + "right_gravity": true, + "priority": 10, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 4, + 4, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 5, + 5, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 6, + 8, + 0, + { + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-05 14:09:08)", "OpencodeHint"], + [" [msg_b8e7d823f001HX1Rr9DYE7TYBf]", "OpencodeHint"] + ], + "right_gravity": true, + "priority": 10, + "virt_text_repeat_linebreak": false, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 7, + 10, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 8, + 11, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 9, + 12, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 10, + 13, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 11, + 14, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 12, + 15, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 13, + 16, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 14, + 17, + 0, + { + "ns_id": 3, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ] + ], + "timestamp": 1767622360, + "lines": [ + "----", + "", + "", + "@no-trust run a git status and tell me what files are changed", + "", + "[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)", + "", + "----", + "", + "", + "** run** `Shows working tree status`", + "", + "`````bash", + "> git status", + "", + "`````", + "", + "> [!ERROR] Error: The user rejected permission to use this specific tool call.", + "", + "" + ], + "actions": [] +} diff --git a/tests/data/permission-ask-new-deny.json b/tests/data/permission-ask-new-deny.json index ad97fce3..00bedda4 100644 --- a/tests/data/permission-ask-new-deny.json +++ b/tests/data/permission-ask-new-deny.json @@ -1 +1,391 @@ -[{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","id":"msg_b8e7d8222001ZAc4G1t5RfHzou","agent":"build","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","time":{"created":1767622148643}}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d8223001xEcei9tomaINdJ","type":"text","messageID":"msg_b8e7d8222001ZAc4G1t5RfHzou","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","text":"@no-trust run a git status and tell me what files are changed"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d8223002NzjiAm7x7Zcg8b","type":"text","synthetic":true,"messageID":"msg_b8e7d8222001ZAc4G1t5RfHzou","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d82270012OhYhykY53tJuV","type":"text","synthetic":true,"messageID":"msg_b8e7d8222001ZAc4G1t5RfHzou","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","text":"\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d8227002hZiHYfLznqKXjU","type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua","mime":"text/plain","messageID":"msg_b8e7d8222001ZAc4G1t5RfHzou","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","filename":"lua/opencode/ui/formatter.lua"}}},{"type":"session.updated","properties":{"info":{"title":"New session - 2026-01-05T14:09:03.597Z","time":{"created":1767622143597,"updated":1767622148667},"version":"1.1.2","id":"ses_471829192ffebHW9UmQGzvCDMj","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"message.updated","properties":{"info":{"parentID":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"assistant","time":{"created":1767622148672},"path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","mode":"build","modelID":"gpt-4.1","providerID":"github-copilot","agent":"build","tokens":{"cache":{"read":0,"write":0},"output":0,"input":0,"reasoning":0},"id":"msg_b8e7d823f001HX1Rr9DYE7TYBf","cost":0}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"user","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","time":{"created":1767622148643},"summary":{"diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","agent":"build"}}},{"type":"session.updated","properties":{"info":{"version":"1.1.2","time":{"created":1767622143597,"updated":1767622148771},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"additions":0,"files":0,"deletions":0},"id":"ses_471829192ffebHW9UmQGzvCDMj","title":"New session - 2026-01-05T14:09:03.597Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d8948001TJr45cBt0IJmwi","type":"step-start","snapshot":"f28b6ec26f4d2f0f1c8fdb665276f5628facc967","messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf","sessionID":"ses_471829192ffebHW9UmQGzvCDMj"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d894b001iax8ZxtH2hvz0B","type":"tool","tool":"bash","messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf","state":{"status":"pending","input":{},"raw":""},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","callID":"call_WjZkpS6d8ZVtA7m8RrDBzA1J"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d894b001iax8ZxtH2hvz0B","type":"tool","tool":"bash","messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf","state":{"status":"running","input":{"command":"git status","description":"Shows working tree status"},"time":{"start":1767622150547}},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","callID":"call_WjZkpS6d8ZVtA7m8RrDBzA1J"}}},{"type":"permission.asked","properties":{"patterns":["git status"],"id":"per_b8e7d89ad001fysFENZJxvDKzb","tool":{"messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf","callID":"call_WjZkpS6d8ZVtA7m8RrDBzA1J"},"permission":"bash","always":["git status*"],"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","metadata":{}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"permission.replied","properties":{"requestID":"per_b8e7d89ad001fysFENZJxvDKzb","reply":"reject","sessionID":"ses_471829192ffebHW9UmQGzvCDMj"}},{"type":"message.part.updated","properties":{"part":{"id":"prt_b8e7d894b001iax8ZxtH2hvz0B","type":"tool","tool":"bash","messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf","state":{"status":"error","error":"Error: The user rejected permission to use this specific tool call.","input":{"command":"git status","description":"Shows working tree status"},"time":{"start":1767622150547,"end":1767622156196}},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","callID":"call_WjZkpS6d8ZVtA7m8RrDBzA1J"}}},{"type":"message.part.updated","properties":{"part":{"cost":0,"id":"prt_b8e7d9fa7001vEc0lpWy1MDUdb","type":"step-finish","snapshot":"f28b6ec26f4d2f0f1c8fdb665276f5628facc967","reason":"tool-calls","tokens":{"cache":{"read":0,"write":0},"output":23,"input":20614,"reasoning":0},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","messageID":"msg_b8e7d823f001HX1Rr9DYE7TYBf"}}},{"type":"message.updated","properties":{"info":{"parentID":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"assistant","finish":"tool-calls","time":{"created":1767622148672},"path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","mode":"build","modelID":"gpt-4.1","providerID":"github-copilot","agent":"build","tokens":{"cache":{"read":0,"write":0},"output":23,"input":20614,"reasoning":0},"id":"msg_b8e7d823f001HX1Rr9DYE7TYBf","cost":0}}},{"type":"message.updated","properties":{"info":{"parentID":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"assistant","finish":"tool-calls","time":{"created":1767622148672,"completed":1767622156230},"path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"sessionID":"ses_471829192ffebHW9UmQGzvCDMj","mode":"build","modelID":"gpt-4.1","providerID":"github-copilot","agent":"build","tokens":{"cache":{"read":0,"write":0},"output":23,"input":20614,"reasoning":0},"id":"msg_b8e7d823f001HX1Rr9DYE7TYBf","cost":0}}},{"type":"session.idle","properties":{"sessionID":"ses_471829192ffebHW9UmQGzvCDMj"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"user","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","time":{"created":1767622148643},"summary":{"diffs":[]},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","agent":"build"}}},{"type":"session.updated","properties":{"info":{"version":"1.1.2","time":{"created":1767622143597,"updated":1767622156244},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"additions":0,"files":0,"deletions":0},"id":"ses_471829192ffebHW9UmQGzvCDMj","title":"New session - 2026-01-05T14:09:03.597Z","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"user","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","time":{"created":1767622148643},"summary":{"diffs":[],"title":"Requesting git status and listing changed files"},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","agent":"build"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"id":"msg_b8e7d8222001ZAc4G1t5RfHzou","role":"user","sessionID":"ses_471829192ffebHW9UmQGzvCDMj","time":{"created":1767622148643},"summary":{"diffs":[],"title":"Running git status to list changed files"},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","agent":"build"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"version":"1.1.2","time":{"created":1767622143597,"updated":1767622165039},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"additions":0,"files":0,"deletions":0},"id":"ses_471829192ffebHW9UmQGzvCDMj","title":"Listing changed files with git status","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]}] +[ + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "id": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "agent": "build", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "time": { "created": 1767622148643 } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d8223001xEcei9tomaINdJ", + "type": "text", + "messageID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "text": "@no-trust run a git status and tell me what files are changed" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d8223002NzjiAm7x7Zcg8b", + "type": "text", + "synthetic": true, + "messageID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d82270012OhYhykY53tJuV", + "type": "text", + "synthetic": true, + "messageID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "text": "\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d8227002hZiHYfLznqKXjU", + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua", + "mime": "text/plain", + "messageID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "filename": "lua/opencode/ui/formatter.lua" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "New session - 2026-01-05T14:09:03.597Z", + "time": { "created": 1767622143597, "updated": 1767622148667 }, + "version": "1.1.2", + "id": "ses_471829192ffebHW9UmQGzvCDMj", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "parentID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "assistant", + "time": { "created": 1767622148672 }, + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "mode": "build", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "agent": "build", + "tokens": { "cache": { "read": 0, "write": 0 }, "output": 0, "input": 0, "reasoning": 0 }, + "id": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "cost": 0 + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "user", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "time": { "created": 1767622148643 }, + "summary": { "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "agent": "build" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "1.1.2", + "time": { "created": 1767622143597, "updated": 1767622148771 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "additions": 0, "files": 0, "deletions": 0 }, + "id": "ses_471829192ffebHW9UmQGzvCDMj", + "title": "New session - 2026-01-05T14:09:03.597Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d8948001TJr45cBt0IJmwi", + "type": "step-start", + "snapshot": "f28b6ec26f4d2f0f1c8fdb665276f5628facc967", + "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d894b001iax8ZxtH2hvz0B", + "type": "tool", + "tool": "bash", + "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "state": { "status": "pending", "input": {}, "raw": "" }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "callID": "call_WjZkpS6d8ZVtA7m8RrDBzA1J" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d894b001iax8ZxtH2hvz0B", + "type": "tool", + "tool": "bash", + "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "state": { + "status": "running", + "input": { "command": "git status", "description": "Shows working tree status" }, + "time": { "start": 1767622150547 } + }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "callID": "call_WjZkpS6d8ZVtA7m8RrDBzA1J" + } + } + }, + { + "type": "permission.asked", + "properties": { + "patterns": ["git status"], + "id": "per_b8e7d89ad001fysFENZJxvDKzb", + "tool": { "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf", "callID": "call_WjZkpS6d8ZVtA7m8RrDBzA1J" }, + "permission": "bash", + "always": ["git status*"], + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "metadata": {} + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "permission.replied", + "properties": { + "requestID": "per_b8e7d89ad001fysFENZJxvDKzb", + "reply": "reject", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj" + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_b8e7d894b001iax8ZxtH2hvz0B", + "type": "tool", + "tool": "bash", + "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "state": { + "status": "error", + "error": "Error: The user rejected permission to use this specific tool call.", + "input": { "command": "git status", "description": "Shows working tree status" }, + "time": { "start": 1767622150547, "end": 1767622156196 } + }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "callID": "call_WjZkpS6d8ZVtA7m8RrDBzA1J" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "cost": 0, + "id": "prt_b8e7d9fa7001vEc0lpWy1MDUdb", + "type": "step-finish", + "snapshot": "f28b6ec26f4d2f0f1c8fdb665276f5628facc967", + "reason": "tool-calls", + "tokens": { "cache": { "read": 0, "write": 0 }, "output": 23, "input": 20614, "reasoning": 0 }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "messageID": "msg_b8e7d823f001HX1Rr9DYE7TYBf" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "parentID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "assistant", + "finish": "tool-calls", + "time": { "created": 1767622148672 }, + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "mode": "build", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "agent": "build", + "tokens": { "cache": { "read": 0, "write": 0 }, "output": 23, "input": 20614, "reasoning": 0 }, + "id": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "cost": 0 + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "parentID": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "assistant", + "finish": "tool-calls", + "time": { "created": 1767622148672, "completed": 1767622156230 }, + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "mode": "build", + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "agent": "build", + "tokens": { "cache": { "read": 0, "write": 0 }, "output": 23, "input": 20614, "reasoning": 0 }, + "id": "msg_b8e7d823f001HX1Rr9DYE7TYBf", + "cost": 0 + } + } + }, + { "type": "session.idle", "properties": { "sessionID": "ses_471829192ffebHW9UmQGzvCDMj" } }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "user", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "time": { "created": 1767622148643 }, + "summary": { "diffs": [] }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "agent": "build" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "1.1.2", + "time": { "created": 1767622143597, "updated": 1767622156244 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "additions": 0, "files": 0, "deletions": 0 }, + "id": "ses_471829192ffebHW9UmQGzvCDMj", + "title": "New session - 2026-01-05T14:09:03.597Z", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "user", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "time": { "created": 1767622148643 }, + "summary": { "diffs": [], "title": "Requesting git status and listing changed files" }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "agent": "build" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "id": "msg_b8e7d8222001ZAc4G1t5RfHzou", + "role": "user", + "sessionID": "ses_471829192ffebHW9UmQGzvCDMj", + "time": { "created": 1767622148643 }, + "summary": { "diffs": [], "title": "Running git status to list changed files" }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "agent": "build" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "version": "1.1.2", + "time": { "created": 1767622143597, "updated": 1767622165039 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "additions": 0, "files": 0, "deletions": 0 }, + "id": "ses_471829192ffebHW9UmQGzvCDMj", + "title": "Listing changed files with git status", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/permission-ask-new.expected.json b/tests/data/permission-ask-new.expected.json index 1c9edfe9..19325386 100644 --- a/tests/data/permission-ask-new.expected.json +++ b/tests/data/permission-ask-new.expected.json @@ -1 +1,453 @@ -{"extmarks":[[1,1,0,{"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-05 14:07:54)","OpencodeHint"],[" [msg_b8e7c60a2001Kisjwk2mVB4dye]","OpencodeHint"]],"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"virt_text_pos":"win_col"}],[2,2,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[3,3,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[4,4,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[5,5,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[6,6,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[7,7,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[8,8,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[9,10,0,{"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-05 14:07:54)","OpencodeHint"],[" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]","OpencodeHint"]],"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"virt_text_pos":"win_col"}],[10,12,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[11,13,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[12,14,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[13,15,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[14,16,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[15,17,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_win_col":-1,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[16,20,0,{"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleSystem"],[" "],["SYSTEM","OpencodeMessageRoleSystem"],["","OpencodeHint"],["","OpencodeHint"],[" [permission-display-message]","OpencodeHint"]],"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text_hide":false,"virt_text_pos":"win_col"}],[17,22,0,{"line_hl_group":"OpencodePermissionTitle","right_gravity":true,"priority":4096,"ns_id":3}],[18,22,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[19,23,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[20,24,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[21,25,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[22,26,0,{"line_hl_group":"OpencodeDialogOptionHover","right_gravity":true,"priority":4096,"ns_id":3}],[23,26,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[24,26,2,{"virt_text_repeat_linebreak":false,"right_gravity":true,"virt_text":[["› ","OpencodeDialogOptionHover"]],"ns_id":3,"priority":4096,"virt_text_hide":false,"virt_text_pos":"overlay"}],[25,27,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[26,28,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[27,29,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}],[28,30,0,{"virt_text_repeat_linebreak":true,"right_gravity":true,"virt_text":[["▌","OpencodePermissionBorder"]],"ns_id":3,"virt_text_win_col":-2,"priority":4096,"virt_text_hide":false,"virt_text_pos":"win_col"}]],"actions":[],"lines":["----","","","@no-trust run a git status and tell me what files are changed","","[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)","","**Diagnostics:**  (11)","","----","","","** run** `Shows working tree status`","","`````bash","> git status","","`````","","----","",""," Permission Required",""," *bash* `git status`",""," 1. Allow once "," 2. Reject"," 3. Allow always","","Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`","",""],"timestamp":1769449848} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-05 14:07:54)", "OpencodeHint"], + [" [msg_b8e7c60a2001Kisjwk2mVB4dye]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 2, + 2, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 3, + 3, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 4, + 4, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 5, + 5, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 6, + 6, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 7, + 7, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 8, + 8, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 9, + 10, + 0, + { + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-05 14:07:54)", "OpencodeHint"], + [" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 10, + 12, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 11, + 13, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 12, + 14, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 13, + 15, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 14, + 16, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 15, + 17, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 16, + 20, + 0, + { + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleSystem"], + [" "], + ["SYSTEM", "OpencodeMessageRoleSystem"], + ["", "OpencodeHint"], + ["", "OpencodeHint"], + [" [permission-display-message]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [17, 22, 0, { "line_hl_group": "OpencodePermissionTitle", "right_gravity": true, "priority": 4096, "ns_id": 3 }], + [ + 18, + 22, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 19, + 23, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 20, + 24, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 21, + 25, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [22, 26, 0, { "line_hl_group": "OpencodeDialogOptionHover", "right_gravity": true, "priority": 4096, "ns_id": 3 }], + [ + 23, + 26, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 24, + 26, + 2, + { + "virt_text_repeat_linebreak": false, + "right_gravity": true, + "virt_text": [["› ", "OpencodeDialogOptionHover"]], + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "overlay" + } + ], + [ + 25, + 27, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 26, + 28, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 27, + 29, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ], + [ + 28, + 30, + 0, + { + "virt_text_repeat_linebreak": true, + "right_gravity": true, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "ns_id": 3, + "virt_text_win_col": -2, + "priority": 4096, + "virt_text_hide": false, + "virt_text_pos": "win_col" + } + ] + ], + "actions": [], + "lines": [ + "----", + "", + "", + "@no-trust run a git status and tell me what files are changed", + "", + "[`lua/opencode/ui/formatter.lua`](lua/opencode/ui/formatter.lua)", + "", + "**Diagnostics:**  (11)", + "", + "----", + "", + "", + "** run** `Shows working tree status`", + "", + "`````bash", + "> git status", + "", + "`````", + "", + "----", + "", + "", + " Permission Required", + "", + " *bash* `git status`", + "", + " 1. Allow once ", + " 2. Reject", + " 3. Allow always", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", + "", + "" + ], + "timestamp": 1769449848 +} diff --git a/tests/data/permission-ask-new.json b/tests/data/permission-ask-new.json index d4e9d979..9b1187ea 100644 --- a/tests/data/permission-ask-new.json +++ b/tests/data/permission-ask-new.json @@ -1 +1,282 @@ -[{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"updated":1767622072149,"created":1767622072149}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","time":{"created":1767622074530}}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3001M2zsH9REfPdlQt","text":"@no-trust run a git status and tell me what files are changed","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3003Ta9NcXkN0lb0lF","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a7001d3WhKOu2vCR4Cf","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n","synthetic":true}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua","id":"prt_b8e7c60a7002ybV09VDHoQQeO7","filename":"lua/opencode/ui/formatter.lua","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","mime":"text/plain"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"text","id":"prt_b8e7c60a3002XHHlk77vAPApvR","messageID":"msg_b8e7c60a2001Kisjwk2mVB4dye","text":"{\"content\":[{\"msg\":\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l490:c43\"},{\"msg\":\"expected `integer` but found `number`.\",\"severity\":2,\"pos\":\"l492:c45\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l492:c62\"},{\"msg\":\"state.active_session may be nil\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"state.messages[#state.messages] may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"state.messages may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"Undefined field `[diag.severity]`.\",\"severity\":2,\"pos\":\"l435:c42\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l526:c34\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l527:c47\"},{\"msg\":\"Cannot assign `number` to `integer`.\",\"severity\":2,\"pos\":\"l493:c5\"}],\"context_type\":\"diagnostics\"}","synthetic":true}},"type":"message.part.updated"},{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"updated":1767622074603,"created":1767622072149}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"providerID":"github-copilot","role":"assistant","path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"agent":"build","time":{"created":1767622074609},"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","mode":"build","modelID":"gpt-4.1","id":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","parentID":"msg_b8e7c60a2001Kisjwk2mVB4dye","cost":0,"tokens":{"output":0,"reasoning":0,"input":0,"cache":{"write":0,"read":0}}}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"diffs":[]}}},"type":"message.updated"},{"properties":{"info":{"title":"New session - 2026-01-05T14:07:52.149Z","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622074670,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":0,"files":0}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"step-start","id":"prt_b8e7c6672001F10WAiQKbzNEtE","snapshot":"5ba6e95774829e7f501299039b72b72dc32c6620","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"tool","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7c6686001cCV44vsFtbpF42","state":{"status":"pending","input":{},"raw":""},"callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx","tool":"bash"}},"type":"message.part.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"part":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","type":"tool","messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","id":"prt_b8e7c6686001cCV44vsFtbpF42","state":{"status":"running","input":{"description":"Shows working tree status","command":"git status"},"time":{"start":1767622076189}},"callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx","tool":"bash"}},"type":"message.part.updated"},{"properties":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","tool":{"messageID":"msg_b8e7c60f1001aEWYlAaDRXQ4aJ","callID":"call_KAC0LdAxKEbU6HTAPH5QJfGx"},"patterns":["git status"],"id":"per_b8e7c6744001DX6EZ9KHM15RLi","permission":"bash","always":["git status*"],"metadata":{}},"type":"permission.asked"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"sessionID":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"created":1767622074530},"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"role":"user","id":"msg_b8e7c60a2001Kisjwk2mVB4dye","agent":"build","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","summary":{"title":"Running git status to list changed files","diffs":[]}}},"type":"message.updated"},{"properties":[],"type":"custom.emit_events.finished"},{"properties":{"info":{"title":"Running git status for formatter.lua changes","version":"1.1.2","directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_47183a8abffeZzOLeJ3j2KV8f8","time":{"updated":1767622082619,"created":1767622072149},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"additions":0,"files":0}}},"type":"session.updated"},{"properties":[],"type":"custom.emit_events.finished"}] +[ + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "updated": 1767622072149, "created": 1767622072149 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "time": { "created": 1767622074530 } + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3001M2zsH9REfPdlQt", + "text": "@no-trust run a git status and tell me what files are changed", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3003Ta9NcXkN0lb0lF", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a7001d3WhKOu2vCR4Cf", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "\n00001| local context_module = require('opencode.context')\n00002| local icons = require('opencode.ui.icons')\n00003| local util = require('opencode.util')\n00004| local Output = require('opencode.ui.output')\n00005| local state = require('opencode.state')\n00006| local config = require('opencode.config')\n00007| local snapshot = require('opencode.snapshot')\n00008| local mention = require('opencode.ui.mention')\n00009| \n00010| local M = {}\n00011| \n00012| M.separator = {\n00013| '----',\n00014| '',\n00015| }\n00016| \n00017| ---@param output Output\n00018| ---@param part OpencodeMessagePart\n00019| function M._format_reasoning(output, part)\n00020| local text = vim.trim(part.text or '')\n00021| if text == '' then\n00022| return\n00023| end\n00024| \n00025| local start_line = output:get_line_count() + 1\n00026| \n00027| local title = 'Reasoning'\n00028| local time = part.time\n00029| if time and type(time) == 'table' and time.start then\n00030| local start_text = util.format_time(time.start) or ''\n00031| local end_text = (time['end'] and util.format_time(time['end'])) or nil\n00032| if end_text and end_text ~= '' then\n00033| title = string.format('%s (%s - %s)', title, start_text, end_text)\n00034| elseif start_text ~= '' then\n00035| title = string.format('%s (%s)', title, start_text)\n00036| end\n00037| end\n00038| \n00039| M._format_action(output, icons.get('reasoning') .. ' ' .. title, '')\n00040| \n00041| if config.ui.output.tools.show_reasoning_output then\n00042| output:add_empty_line()\n00043| output:add_lines(vim.split(text, '\\n'))\n00044| output:add_empty_line()\n00045| end\n00046| \n00047| local end_line = output:get_line_count()\n00048| if end_line - start_line > 1 then\n00049| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')\n00050| else\n00051| output:add_extmark(start_line - 1, {\n00052| line_hl_group = 'OpencodeReasoningText',\n00053| } --[[@as OutputExtmark]])\n00054| end\n00055| end\n00056| \n00057| function M._handle_permission_request(output, part)\n00058| if part.state and part.state.status == 'error' and part.state.error then\n00059| if part.state.error:match('rejected permission') then\n00060| state.current_permission = nil\n00061| else\n00062| vim.notify('Unknown part state error: ' .. part.state.error)\n00063| end\n00064| return\n00065| end\n00066| \n00067| M._format_permission_request(output)\n00068| end\n00069| \n00070| function M._format_permission_request(output)\n00071| local keys\n00072| \n00073| if require('opencode.ui.ui').is_opencode_focused() then\n00074| keys = {\n00075| config.keymap.permission.accept,\n00076| config.keymap.permission.accept_all,\n00077| config.keymap.permission.deny,\n00078| }\n00079| else\n00080| keys = {\n00081| config.get_key_for_function('editor', 'permission_accept'),\n00082| config.get_key_for_function('editor', 'permission_accept_all'),\n00083| config.get_key_for_function('editor', 'permission_deny'),\n00084| }\n00085| end\n00086| \n00087| output:add_empty_line()\n00088| output:add_line('> [!WARNING] Permission required to run this tool.')\n00089| output:add_line('>')\n00090| output:add_line(('> Accept `%s` Always `%s` Deny `%s`'):format(unpack(keys)))\n00091| output:add_empty_line()\n00092| end\n00093| \n00094| ---Calculate statistics for reverted messages and tool calls\n00095| ---@param messages {info: MessageInfo, parts: OpencodeMessagePart[]}[] All messages in the session\n00096| ---@param revert_index number Index of the message where revert occurred\n00097| ---@param revert_info SessionRevertInfo Revert information\n00098| ---@return {messages: number, tool_calls: number, files: table}\n00099| function M._calculate_revert_stats(messages, revert_index, revert_info)\n00100| local stats = {\n00101| messages = 0,\n00102| tool_calls = 0,\n00103| files = {}, -- { [filename] = { additions = n, deletions = m } }\n00104| }\n00105| \n00106| for i = revert_index, #messages do\n00107| local msg = messages[i]\n00108| if msg.info.role == 'user' then\n00109| stats.messages = stats.messages + 1\n00110| end\n00111| if msg.parts then\n00112| for _, part in ipairs(msg.parts) do\n00113| if part.type == 'tool' then\n00114| stats.tool_calls = stats.tool_calls + 1\n00115| end\n00116| end\n00117| end\n00118| end\n00119| \n00120| if revert_info.diff then\n00121| local current_file = nil\n00122| for line in revert_info.diff:gmatch('[^\\r\\n]+') do\n00123| local file_a = line:match('^%-%-%- ([ab]/.+)')\n00124| local file_b = line:match('^%+%+%+ ([ab]/.+)')\n00125| if file_b then\n00126| current_file = file_b:gsub('^[ab]/', '')\n00127| if not stats.files[current_file] then\n00128| stats.files[current_file] = { additions = 0, deletions = 0 }\n00129| end\n00130| elseif file_a then\n00131| current_file = file_a:gsub('^[ab]/', '')\n00132| if not stats.files[current_file] then\n00133| stats.files[current_file] = { additions = 0, deletions = 0 }\n00134| end\n00135| elseif line:sub(1, 1) == '+' and not line:match('^%+%+%+') then\n00136| if current_file then\n00137| stats.files[current_file].additions = stats.files[current_file].additions + 1\n00138| end\n00139| elseif line:sub(1, 1) == '-' and not line:match('^%-%-%-') then\n00140| if current_file then\n00141| stats.files[current_file].deletions = stats.files[current_file].deletions + 1\n00142| end\n00143| end\n00144| end\n00145| end\n00146| \n00147| return stats\n00148| end\n00149| \n00150| ---Format the revert callout with statistics\n00151| ---@param session_data OpencodeMessage[] All messages in the session\n00152| ---@param start_idx number Index of the message where revert occurred\n00153| ---@return Output output object representing the lines, extmarks, and actions\n00154| function M._format_revert_message(session_data, start_idx)\n00155| local output = Output.new()\n00156| local stats = M._calculate_revert_stats(session_data, start_idx, state.active_session.revert)\n00157| local message_text = stats.messages == 1 and 'message' or 'messages'\n00158| local tool_text = stats.tool_calls == 1 and 'tool call' or 'tool calls'\n00159| \n00160| output:add_lines(M.separator)\n00161| output:add_line(\n00162| string.format('> %d %s reverted, %d %s reverted', stats.messages, message_text, stats.tool_calls, tool_text)\n00163| )\n00164| output:add_line('>')\n00165| output:add_line('> type `/redo` to restore.')\n00166| output:add_empty_line()\n00167| \n00168| if stats.files and next(stats.files) then\n00169| for file, fstats in pairs(stats.files) do\n00170| local file_diff = {}\n00171| if fstats.additions > 0 then\n00172| table.insert(file_diff, '+' .. fstats.additions)\n00173| end\n00174| if fstats.deletions > 0 then\n00175| table.insert(file_diff, '-' .. fstats.deletions)\n00176| end\n00177| if #file_diff > 0 then\n00178| local line_str = string.format(icons.get('file') .. '%s: %s', file, table.concat(file_diff, ' '))\n00179| local line_idx = output:add_line(line_str)\n00180| local col = #(' ' .. file .. ': ')\n00181| for _, diff in ipairs(file_diff) do\n00182| local hl_group = diff:sub(1, 1) == '+' and 'OpencodeDiffAddText' or 'OpencodeDiffDeleteText'\n00183| output:add_extmark(line_idx - 1, {\n00184| virt_text = { { diff, hl_group } },\n00185| virt_text_pos = 'inline',\n00186| virt_text_win_col = col,\n00187| priority = 1000,\n00188| } --[[@as OutputExtmark]])\n00189| col = col + #diff + 1\n00190| end\n00191| end\n00192| end\n00193| end\n00194| return output\n00195| end\n00196| \n00197| local function add_action(output, text, action_type, args, key, line)\n00198| -- actions use api-indexing (e.g. 0 indexed)\n00199| line = (line or output:get_line_count()) - 1\n00200| output:add_action({\n00201| text = text,\n00202| type = action_type,\n00203| args = args,\n00204| key = key,\n00205| display_line = line,\n00206| range = { from = line, to = line },\n00207| })\n00208| end\n00209| \n00210| ---@param output Output Output object to write to\n00211| ---@param part OpencodeMessagePart\n00212| function M._format_patch(output, part)\n00213| if not part.hash then\n00214| return\n00215| end\n00216| \n00217| local restore_points = snapshot.get_restore_points_by_parent(part.hash) or {}\n00218| M._format_action(output, icons.get('snapshot') .. ' Created Snapshot', vim.trim(part.hash:sub(1, 8)))\n00219| \n00220| -- Anchor all snapshot-level actions to the snapshot header line\n00221| add_action(output, '[R]evert file', 'diff_revert_selected_file', { part.hash }, 'R')\n00222| add_action(output, 'Revert [A]ll', 'diff_revert_all', { part.hash }, 'A')\n00223| add_action(output, '[D]iff', 'diff_open', { part.hash }, 'D')\n00224| \n00225| if #restore_points > 0 then\n00226| for _, restore_point in ipairs(restore_points) do\n00227| output:add_line(\n00228| string.format(\n00229| ' %s Restore point `%s` - %s ',\n00230| icons.get('restore_point'),\n00231| vim.trim(restore_point.id:sub(1, 8)),\n00232| util.format_time(restore_point.created_at)\n00233| )\n00234| )\n00235| add_action(output, 'Restore [A]ll', 'diff_restore_snapshot_all', { restore_point.id }, 'A')\n00236| add_action(output, '[R]estore file', 'diff_restore_snapshot_file', { restore_point.id }, 'R')\n00237| end\n00238| end\n00239| end\n00240| \n00241| ---@param output Output Output object to write to\n00242| ---@param message MessageInfo\n00243| function M._format_error(output, message)\n00244| output:add_empty_line()\n00245| M._format_callout(output, 'ERROR', vim.inspect(message.error))\n00246| end\n00247| \n00248| ---@param message OpencodeMessage\n00249| ---@return Output\n00250| function M.format_message_header(message)\n00251| local output = Output.new()\n00252| \n00253| output:add_lines(M.separator)\n00254| local role = message.info.role or 'unknown'\n00255| local icon = message.info.role == 'user' and icons.get('header_user') or icons.get('header_assistant')\n00256| \n00257| local time = message.info.time and message.info.time.created or nil\n00258| local time_text = (time and ' (' .. util.format_time(time) .. ')' or '')\n00259| local role_hl = 'OpencodeMessageRole' .. role:sub(1, 1):upper() .. role:sub(2)\n00260| local model_text = message.info.modelID and ' ' .. message.info.modelID or ''\n00261| \n00262| local debug_text = config.debug.show_ids and ' [' .. message.info.id .. ']' or ''\n00263| \n00264| local display_name\n00265| if role == 'assistant' then\n00266| local mode = message.info.mode\n00267| if mode and mode ~= '' then\n00268| display_name = mode:upper()\n00269| else\n00270| -- For the most recent assistant message, show current_mode if mode is missing\n00271| -- This handles new messages that haven't been stamped yet\n00272| local is_last_message = #state.messages == 0 or message.info.id == state.messages[#state.messages].info.id\n00273| if is_last_message and state.current_mode and state.current_mode ~= '' then\n00274| display_name = state.current_mode:upper()\n00275| else\n00276| display_name = 'ASSISTANT'\n00277| end\n00278| end\n00279| else\n00280| display_name = role:upper()\n00281| end\n00282| \n00283| output:add_extmark(output:get_line_count() - 1, {\n00284| virt_text = {\n00285| { icon, role_hl },\n00286| { ' ' },\n00287| { display_name, role_hl },\n00288| { model_text, 'OpencodeHint' },\n00289| { time_text, 'OpencodeHint' },\n00290| { debug_text, 'OpencodeHint' },\n00291| },\n00292| virt_text_win_col = -3,\n00293| priority = 10,\n00294| } --[[@as OutputExtmark]])\n00295| \n00296| -- Only want to show the error if we have no parts. If we have parts, they'll\n00297| -- handle rendering the error\n00298| if\n00299| role == 'assistant'\n00300| and message.info.error\n00301| and message.info.error ~= ''\n00302| and (not message.parts or #message.parts == 0)\n00303| then\n00304| local error = message.info.error\n00305| local error_message = error.data and error.data.message or vim.inspect(error)\n00306| \n00307| output:add_line('')\n00308| M._format_callout(output, 'ERROR', error_message)\n00309| end\n00310| \n00311| output:add_line('')\n00312| return output\n00313| end\n00314| \n00315| ---@param output Output Output object to write to\n00316| ---@param callout string Callout type (e.g., 'ERROR', 'TODO')\n00317| ---@param text string Callout text content\n00318| ---@param title? string Optional title for the callout\n00319| function M._format_callout(output, callout, text, title)\n00320| title = title and title .. ' ' or ''\n00321| local win_width = (state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win))\n00322| and vim.api.nvim_win_get_width(state.windows.output_win)\n00323| or config.ui.window_width\n00324| or 80\n00325| if #text > win_width - 4 then\n00326| local ok, substituted = pcall(vim.fn.substitute, text, '\\v(.{' .. (win_width - 8) .. '})', '\\1\\n', 'g')\n00327| text = ok and substituted or text\n00328| end\n00329| \n00330| -- Trim off any trailing newlines so there isn't an extra line in the\n00331| -- extmarks section\n00332| local lines = vim.split(text:gsub('\\n$', ''), '\\n')\n00333| if #lines == 1 and title == '' then\n00334| output:add_line('> [!' .. callout .. '] ' .. lines[1])\n00335| else\n00336| output:add_line('> [!' .. callout .. ']' .. title)\n00337| output:add_line('>')\n00338| output:add_lines(lines, '> ')\n00339| end\n00340| end\n00341| \n00342| ---@param output Output Output object to write to\n00343| ---@param text string\n00344| ---@param message? OpencodeMessage Optional message object to extract mentions from\n00345| function M._format_user_prompt(output, text, message)\n00346| local start_line = output:get_line_count()\n00347| \n00348| output:add_lines(vim.split(text, '\\n'))\n00349| \n00350| local end_line = output:get_line_count()\n00351| \n00352| local end_line_extmark_offset = 0\n00353| \n00354| local mentions = {}\n00355| if message and message.parts then\n00356| -- message.parts will only be filled out on a re-render\n00357| -- we need to collect the mentions here\n00358| for _, part in ipairs(message.parts) do\n00359| if part.type == 'file' then\n00360| -- we're rerendering this part and we have files, the space after the user prompt\n00361| -- also needs an extmark\n00362| end_line_extmark_offset = 1\n00363| if part.source and part.source.text then\n00364| table.insert(mentions, part.source.text)\n00365| end\n00366| elseif part.type == 'agent' then\n00367| if part.source then\n00368| table.insert(mentions, part.source)\n00369| end\n00370| end\n00371| end\n00372| end\n00373| \n00374| if #mentions > 0 then\n00375| mention.highlight_mentions_in_output(output, text, mentions, start_line)\n00376| end\n00377| \n00378| M._add_vertical_border(output, start_line, end_line + end_line_extmark_offset, 'OpencodeMessageRoleUser', -3)\n00379| end\n00380| \n00381| ---@param output Output Output object to write to\n00382| ---@param part OpencodeMessagePart\n00383| function M._format_selection_context(output, part)\n00384| local json = context_module.decode_json_context(part.text or '', 'selection')\n00385| if not json then\n00386| return\n00387| end\n00388| local start_line = output:get_line_count()\n00389| output:add_lines(vim.split(json.content or '', '\\n'))\n00390| output:add_empty_line()\n00391| \n00392| local end_line = output:get_line_count()\n00393| \n00394| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00395| end\n00396| \n00397| ---@param output Output Output object to write to\n00398| ---@param part OpencodeMessagePart\n00399| function M._format_cursor_data_context(output, part)\n00400| local json = context_module.decode_json_context(part.text or '', 'cursor-data')\n00401| if not json then\n00402| return\n00403| end\n00404| local start_line = output:get_line_count()\n00405| output:add_line('Line ' .. json.line .. ':')\n00406| output:add_lines(vim.split(json.line_content or '', '\\n'))\n00407| output:add_empty_line()\n00408| \n00409| local end_line = output:get_line_count()\n00410| \n00411| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00412| end\n00413| \n00414| ---@param output Output Output object to write to\n00415| ---@param part OpencodeMessagePart\n00416| function M._format_diagnostics_context(output, part)\n00417| local json = context_module.decode_json_context(part.text or '', 'diagnostics')\n00418| if not json then\n00419| return\n00420| end\n00421| local start_line = output:get_line_count()\n00422| local diagnostics = json.content --[[@as OpencodeDiagnostic[] ]]\n00423| if not diagnostics or type(diagnostics) ~= 'table' or #diagnostics == 0 then\n00424| return\n00425| end\n00426| \n00427| local diagnostics_count = { error = 0, warn = 0, info = 0 }\n00428| local diagnostics_icons = {\n00429| error = icons.get('error'),\n00430| warn = icons.get('warning'),\n00431| info = icons.get('info'),\n00432| }\n00433| \n00434| for _, diag in ipairs(diagnostics) do\n00435| local name = vim.diagnostic.severity[diag.severity]:lower()\n00436| diagnostics_count[name] = diagnostics_count[name] + 1\n00437| end\n00438| \n00439| local diag_line = '**Diagnostics:**'\n00440| for name, count in pairs(diagnostics_count) do\n00441| if count > 0 then\n00442| diag_line = diag_line .. (string.format(' %s(%d)', diagnostics_icons[name], count))\n00443| end\n00444| end\n00445| output:add_line(diag_line)\n00446| output:add_empty_line()\n00447| local end_line = output:get_line_count()\n00448| \n00449| M._add_vertical_border(output, start_line, end_line, 'OpencodeMessageRoleUser', -3)\n00450| end\n00451| \n00452| ---Format and display the file path in the context\n00453| ---@param output Output Output object to write to\n00454| ---@param path string|nil File path\n00455| function M._format_context_file(output, path)\n00456| if not path or path == '' then\n00457| return\n00458| end\n00459| local cwd = vim.fn.getcwd()\n00460| if vim.startswith(path, cwd) then\n00461| path = path:sub(#cwd + 2)\n00462| end\n00463| return output:add_line(string.format('[%s](%s)', path, path))\n00464| end\n00465| \n00466| ---@param output Output Output object to write to\n00467| ---@param text string\n00468| function M._format_assistant_message(output, text)\n00469| local reference_picker = require('opencode.ui.reference_picker')\n00470| local references = reference_picker.parse_references(text, '')\n00471| \n00472| -- If no references, just add the text as-is\n00473| if #references == 0 then\n00474| output:add_lines(vim.split(text, '\\n'))\n00475| return\n00476| end\n00477| \n00478| -- Sort references by match_start position (ascending)\n00479| table.sort(references, function(a, b)\n00480| return a.match_start < b.match_start\n00481| end)\n00482| \n00483| -- Build a new text with icons inserted before each reference\n00484| local result = ''\n00485| local last_pos = 1\n00486| local ref_icon = icons.get('reference')\n00487| \n00488| for _, ref in ipairs(references) do\n00489| -- Add text before this reference\n00490| result = result .. text:sub(last_pos, ref.match_start - 1)\n00491| -- Add the icon and the reference\n00492| result = result .. ref_icon .. text:sub(ref.match_start, ref.match_end)\n00493| last_pos = ref.match_end + 1\n00494| end\n00495| \n00496| -- Add any remaining text after the last reference\n00497| if last_pos <= #text then\n00498| result = result .. text:sub(last_pos)\n00499| end\n00500| \n00501| output:add_lines(vim.split(result, '\\n'))\n00502| end\n00503| \n00504| ---@param output Output Output object to write to\n00505| ---@param tool_type string Tool type (e.g., 'run', 'read', 'edit', etc.)\n00506| ---@param value string Value associated with the action (e.g., filename, command)\n00507| function M._format_action(output, tool_type, value)\n00508| if not tool_type or not value then\n00509| return\n00510| end\n00511| local line = string.format('**%s** %s', tool_type, value and #value > 0 and ('`' .. value .. '`') or '')\n00512| \n00513| output:add_line(line)\n00514| end\n00515| \n00516| ---@param output Output Output object to write to\n00517| ---@param input BashToolInput data for the tool\n00518| ---@param metadata BashToolMetadata Metadata for the tool use\n00519| function M._format_bash_tool(output, input, metadata)\n00520| M._format_action(output, icons.get('run') .. ' run', input and input.description)\n00521| \n00522| if not config.ui.output.tools.show_output then\n00523| return\n00524| end\n00525| \n00526| if metadata.output or metadata.command or input.command then\n00527| local command = input.command or metadata.command or ''\n00528| local command_output = metadata.output and metadata.output ~= '' and ('\\n' .. metadata.output) or ''\n00529| M._format_code(output, vim.split('> ' .. command .. '\\n' .. command_output, '\\n'), 'bash')\n00530| end\n00531| end\n00532| \n00533| ---@param output Output Output object to write to\n00534| ---@param tool_type string Tool type (e.g., 'read', 'edit', 'write')\n00535| ---@param input FileToolInput data for the tool\n00536| ---@param metadata FileToolMetadata Metadata for the tool use\n00537| function M._format_file_tool(output, tool_type, input, metadata)\n00538| local file_name = input and vim.fn.fnamemodify(input.filePath, ':t') or ''\n00539| local file_type = input and util.get_markdown_filetype(input.filePath) or ''\n00540| local tool_action_icons = { read = icons.get('read'), edit = icons.get('edit'), write = icons.get('write') }\n00541| \n00542| M._format_action(output, tool_action_icons[tool_type] .. ' ' .. tool_type, file_name)\n00543| \n00544| if not config.ui.output.tools.show_output then\n00545| return\n00546| end\n00547| \n00548| if tool_type == 'edit' and metadata.diff then\n00549| M._format_diff(output, metadata.diff, file_type)\n00550| elseif tool_type == 'write' and input and input.content then\n00551| M._format_code(output, vim.split(input.content, '\\n'), file_type)\n00552| end\n00553| end\n00554| \n00555| ---@param output Output Output object to write to\n00556| ---@param title string\n00557| ---@param input TodoToolInput\n00558| function M._format_todo_tool(output, title, input)\n00559| M._format_action(output, icons.get('plan') .. ' plan', (title or ''))\n00560| if not config.ui.output.tools.show_output then\n00561| return\n00562| end\n00563| \n00564| local todos = input and input.todos or {}\n00565| \n00566| for _, item in ipairs(todos) do\n00567| local statuses = { in_progress = '-', completed = 'x', pending = ' ' }\n00568| output:add_line(string.format('- [%s] %s ', statuses[item.status], item.content))\n00569| end\n00570| end\n00571| \n00572| ---@param output Output Output object to write to\n00573| ---@param input GlobToolInput data for the tool\n00574| ---@param metadata GlobToolMetadata Metadata for the tool use\n00575| function M._format_glob_tool(output, input, metadata)\n00576| M._format_action(output, icons.get('search') .. ' glob', input and input.pattern)\n00577| if not config.ui.output.tools.show_output then\n00578| return\n00579| end\n00580| local prefix = metadata.truncated and ' more than' or ''\n00581| output:add_line(string.format('Found%s `%d` file(s):', prefix, metadata.count or 0))\n00582| end\n00583| \n00584| ---@param output Output Output object to write to\n00585| ---@param input GrepToolInput data for the tool\n00586| ---@param metadata GrepToolMetadata Metadata for the tool use\n00587| function M._format_grep_tool(output, input, metadata)\n00588| input = input or { path = '', include = '', pattern = '' }\n00589| \n00590| local grep_str = string.format('%s` `%s', (input.path or input.include) or '', input.pattern or '')\n00591| \n00592| M._format_action(output, icons.get('search') .. ' grep', grep_str)\n00593| if not config.ui.output.tools.show_output then\n00594| return\n00595| end\n00596| local prefix = metadata.truncated and ' more than' or ''\n00597| output:add_line(\n00598| string.format('Found%s `%d` match' .. (metadata.matches ~= 1 and 'es' or ''), prefix, metadata.matches or 0)\n00599| )\n00600| end\n00601| \n00602| ---@param output Output Output object to write to\n00603| ---@param input WebFetchToolInput data for the tool\n00604| function M._format_webfetch_tool(output, input)\n00605| M._format_action(output, icons.get('web') .. ' fetch', input and input.url)\n00606| end\n00607| \n00608| ---@param output Output Output object to write to\n00609| ---@param input ListToolInput\n00610| ---@param metadata ListToolMetadata\n00611| ---@param tool_output string\n00612| function M._format_list_tool(output, input, metadata, tool_output)\n00613| M._format_action(output, icons.get('list') .. ' list', input and input.path or '')\n00614| if not config.ui.output.tools.show_output then\n00615| return\n00616| end\n00617| local lines = vim.split(vim.trim(tool_output or ''), '\\n')\n00618| if #lines < 1 or metadata.count == 0 then\n00619| output:add_line('No files found.')\n00620| return\n00621| end\n00622| if #lines > 1 then\n00623| output:add_line('Files:')\n00624| for i = 2, #lines do\n00625| local file = vim.trim(lines[i])\n00626| if file ~= '' then\n00627| output:add_line(' • ' .. file)\n00628| end\n00629| end\n00630| end\n00631| if metadata.truncated then\n00632| output:add_line(string.format('Results truncated, showing first %d files', metadata.count or '?'))\n00633| end\n00634| end\n00635| \n00636| ---@param output Output Output object to write to\n00637| ---@param part OpencodeMessagePart\n00638| function M._format_tool(output, part)\n00639| local tool = part.tool\n00640| if not tool or not part.state then\n00641| return\n00642| end\n00643| \n00644| local start_line = output:get_line_count() + 1\n00645| local input = part.state.input or {}\n00646| local metadata = part.state.metadata or {}\n00647| local tool_output = part.state.output or ''\n00648| \n00649| if state.current_permission and state.current_permission.messageID == part.messageID then\n00650| metadata = state.current_permission.metadata or metadata\n00651| end\n00652| \n00653| if tool == 'bash' then\n00654| M._format_bash_tool(output, input --[[@as BashToolInput]], metadata --[[@as BashToolMetadata]])\n00655| elseif tool == 'read' or tool == 'edit' or tool == 'write' then\n00656| M._format_file_tool(output, tool, input --[[@as FileToolInput]], metadata --[[@as FileToolMetadata]])\n00657| elseif tool == 'todowrite' then\n00658| M._format_todo_tool(output, part.state.title, input --[[@as TodoToolInput]])\n00659| elseif tool == 'glob' then\n00660| M._format_glob_tool(output, input --[[@as GlobToolInput]], metadata --[[@as GlobToolMetadata]])\n00661| elseif tool == 'list' then\n00662| M._format_list_tool(output, input --[[@as ListToolInput]], metadata --[[@as ListToolMetadata]], tool_output)\n00663| elseif tool == 'grep' then\n00664| M._format_grep_tool(output, input --[[@as GrepToolInput]], metadata --[[@as GrepToolMetadata]])\n00665| elseif tool == 'webfetch' then\n00666| M._format_webfetch_tool(output, input --[[@as WebFetchToolInput]])\n00667| elseif tool == 'task' then\n00668| M._format_task_tool(output, input --[[@as TaskToolInput]], metadata --[[@as TaskToolMetadata]], tool_output)\n00669| else\n00670| M._format_action(output, icons.get('tool') .. ' tool', tool)\n00671| end\n00672| \n00673| if part.state.status == 'error' and part.state.error then\n00674| output:add_line('')\n00675| M._format_callout(output, 'ERROR', part.state.error)\n00676| ---@diagnostic disable-next-line: undefined-field\n00677| elseif part.state.input and part.state.input.error then\n00678| output:add_line('')\n00679| ---I'm not sure about the type with state.input.error\n00680| ---@diagnostic disable-next-line: undefined-field\n00681| M._format_callout(output, 'ERROR', part.state.input.error)\n00682| end\n00683| \n00684| if\n00685| state.current_permission\n00686| and (\n00687| (\n00688| state.current_permission.tool\n00689| and state.current_permission.tool.callID == part.callID\n00690| and state.current_permission.tool.messageID == part.messageID\n00691| )\n00692| ---@TODO this is for backward compatibility, remove later\n00693| or (state.current_permission.messageID == part.messageID and state.current_permission.callID == part.callID)\n00694| )\n00695| then\n00696| M._handle_permission_request(output, part)\n00697| end\n00698| \n00699| local end_line = output:get_line_count()\n00700| if end_line - start_line > 1 then\n00701| M._add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1)\n00702| end\n00703| end\n00704| \n00705| ---@param output Output Output object to write to\n00706| ---@param input TaskToolInput data for the tool\n00707| ---@param metadata TaskToolMetadata Metadata for the tool use\n00708| ---@param tool_output string\n00709| function M._format_task_tool(output, input, metadata, tool_output)\n00710| local start_line = output:get_line_count() + 1\n00711| M._format_action(output, icons.get('task') .. ' task', input and input.description)\n00712| \n00713| if config.ui.output.tools.show_output then\n00714| if tool_output and tool_output ~= '' then\n00715| output:add_empty_line()\n00716| output:add_lines(vim.split(tool_output, '\\n'))\n00717| output:add_empty_line()\n00718| end\n00719| \n00720| if metadata.summary and type(metadata.summary) == 'table' then\n00721| for _, sub_part in ipairs(metadata.summary) do\n00722| if sub_part.type == 'tool' and sub_part.tool then\n00723| M._format_tool(output, sub_part)\n00724| end\n00725| end\n00726| end\n00727| end\n00728| \n00729| local end_line = output:get_line_count()\n00730| output:add_action({\n00731| text = '[S]elect Child Session',\n00732| type = 'select_child_session',\n00733| args = {},\n00734| key = 'S',\n00735| display_line = start_line - 1,\n00736| range = { from = start_line, to = end_line },\n00737| })\n00738| end\n00739| \n00740| ---@param output Output Output object to write to\n00741| ---@param lines string[]\n00742| ---@param language string\n00743| function M._format_code(output, lines, language)\n00744| output:add_empty_line()\n00745| --- NOTE: use longer code fence because lines could contain ```\n00746| output:add_line('`````' .. (language or ''))\n00747| output:add_lines(util.sanitize_lines(lines))\n00748| output:add_line('`````')\n00749| end\n00750| \n00751| ---@param output Output Output object to write to\n00752| ---@param code string\n00753| ---@param file_type string\n00754| function M._format_diff(output, code, file_type)\n00755| output:add_empty_line()\n00756| \n00757| --- NOTE: use longer code fence because code could contain ```\n00758| output:add_line('`````' .. file_type)\n00759| local lines = vim.split(code, '\\n')\n00760| if #lines > 5 then\n00761| lines = vim.list_slice(lines, 6)\n00762| end\n00763| \n00764| for _, line in ipairs(lines) do\n00765| local first_char = line:sub(1, 1)\n00766| if first_char == '+' or first_char == '-' then\n00767| local hl_group = first_char == '+' and 'OpencodeDiffAdd' or 'OpencodeDiffDelete'\n00768| output:add_line(' ' .. line:sub(2))\n00769| local line_idx = output:get_line_count()\n00770| output:add_extmark(line_idx - 1, function()\n00771| return {\n00772| end_col = 0,\n00773| end_row = line_idx,\n00774| virt_text = { { first_char, hl_group } },\n00775| hl_group = hl_group,\n00776| hl_eol = true,\n00777| priority = 5000,\n00778| right_gravity = true,\n00779| end_right_gravity = false,\n00780| virt_text_hide = false,\n00781| virt_text_pos = 'overlay',\n00782| virt_text_repeat_linebreak = false,\n00783| }\n00784| end)\n00785| else\n00786| output:add_line(line)\n00787| end\n00788| end\n00789| output:add_line('`````')\n00790| end\n00791| \n00792| ---@param output Output Output object to write to\n00793| ---@param start_line number\n00794| ---@param end_line number\n00795| ---@param hl_group string Highlight group for the border character\n00796| ---@param win_col number\n00797| ---@param text_hl_group? string Optional highlight group for the background/foreground of text lines\n00798| function M._add_vertical_border(output, start_line, end_line, hl_group, win_col, text_hl_group)\n00799| for line = start_line, end_line do\n00800| local extmark_opts = {\n00801| virt_text = { { require('opencode.ui.icons').get('border'), hl_group } },\n00802| virt_text_pos = 'overlay',\n00803| virt_text_win_col = win_col,\n00804| virt_text_repeat_linebreak = true,\n00805| }\n00806| \n00807| -- Add line highlight if text_hl_group is provided\n00808| if text_hl_group then\n00809| extmark_opts.line_hl_group = text_hl_group\n00810| end\n00811| \n00812| output:add_extmark(line - 1, extmark_opts --[[@as OutputExtmark]])\n00813| end\n00814| end\n00815| \n00816| ---Formats a single message part and returns the resulting output object\n00817| ---@param part OpencodeMessagePart The part to format\n00818| ---@param message? OpencodeMessage Optional message object to extract role and mentions from\n00819| ---@param is_last_part? boolean Whether this is the last part in the message, used to show an error if there is one\n00820| ---@return Output\n00821| function M.format_part(part, message, is_last_part)\n00822| local output = Output.new()\n00823| \n00824| if not message or not message.info or not message.info.role then\n00825| return output\n00826| end\n00827| \n00828| local content_added = false\n00829| local role = message.info.role\n00830| \n00831| if role == 'user' then\n00832| if part.type == 'text' and part.text then\n00833| if part.synthetic == true then\n00834| M._format_selection_context(output, part)\n00835| M._format_cursor_data_context(output, part)\n00836| M._format_diagnostics_context(output, part)\n00837| else\n00838| M._format_user_prompt(output, vim.trim(part.text), message)\n00839| content_added = true\n00840| end\n00841| elseif part.type == 'file' then\n00842| local file_line = M._format_context_file(output, part.filename)\n00843| if file_line then\n00844| M._add_vertical_border(output, file_line - 1, file_line, 'OpencodeMessageRoleUser', -3)\n00845| content_added = true\n00846| end\n00847| end\n00848| elseif role == 'assistant' then\n00849| if part.type == 'text' and part.text then\n00850| M._format_assistant_message(output, vim.trim(part.text))\n00851| content_added = true\n00852| elseif part.type == 'reasoning' and part.text then\n00853| M._format_reasoning(output, part)\n00854| content_added = true\n00855| elseif part.type == 'tool' then\n00856| M._format_tool(output, part)\n00857| content_added = true\n00858| elseif part.type == 'patch' and part.hash then\n00859| M._format_patch(output, part)\n00860| content_added = true\n00861| end\n00862| end\n00863| \n00864| if content_added then\n00865| output:add_empty_line()\n00866| end\n00867| \n00868| if is_last_part and role == 'assistant' and message.info.error and message.info.error ~= '' then\n00869| local error = message.info.error\n00870| local error_message = error.data and error.data.message or vim.inspect(error)\n00871| M._format_callout(output, 'ERROR', error_message)\n00872| output:add_empty_line()\n00873| end\n00874| \n00875| return output\n00876| end\n00877| \n00878| return M\n00879| \n\n(End of file - total 879 lines)\n", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/formatter.lua", + "id": "prt_b8e7c60a7002ybV09VDHoQQeO7", + "filename": "lua/opencode/ui/formatter.lua", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "mime": "text/plain" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "text", + "id": "prt_b8e7c60a3002XHHlk77vAPApvR", + "messageID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "text": "{\"content\":[{\"msg\":\"expected `SessionRevertInfo` but found `SessionRevertInfo?`.\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l490:c43\"},{\"msg\":\"expected `integer` but found `number`.\",\"severity\":2,\"pos\":\"l492:c45\"},{\"msg\":\"expected `integer?` but found `number`.\",\"severity\":2,\"pos\":\"l492:c62\"},{\"msg\":\"state.active_session may be nil\",\"severity\":2,\"pos\":\"l156:c68\"},{\"msg\":\"state.messages[#state.messages] may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"state.messages may be nil\",\"severity\":2,\"pos\":\"l272:c74\"},{\"msg\":\"Undefined field `[diag.severity]`.\",\"severity\":2,\"pos\":\"l435:c42\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l526:c34\"},{\"msg\":\"Undefined field `command`.\",\"severity\":2,\"pos\":\"l527:c47\"},{\"msg\":\"Cannot assign `number` to `integer`.\",\"severity\":2,\"pos\":\"l493:c5\"}],\"context_type\":\"diagnostics\"}", + "synthetic": true + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "updated": 1767622074603, "created": 1767622072149 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "providerID": "github-copilot", + "role": "assistant", + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "agent": "build", + "time": { "created": 1767622074609 }, + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "mode": "build", + "modelID": "gpt-4.1", + "id": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "parentID": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "cost": 0, + "tokens": { "output": 0, "reasoning": 0, "input": 0, "cache": { "write": 0, "read": 0 } } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { "diffs": [] } + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "title": "New session - 2026-01-05T14:07:52.149Z", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622074670, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "step-start", + "id": "prt_b8e7c6672001F10WAiQKbzNEtE", + "snapshot": "5ba6e95774829e7f501299039b72b72dc32c6620", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "tool", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7c6686001cCV44vsFtbpF42", + "state": { "status": "pending", "input": {}, "raw": "" }, + "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx", + "tool": "bash" + } + }, + "type": "message.part.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "part": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "type": "tool", + "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", + "id": "prt_b8e7c6686001cCV44vsFtbpF42", + "state": { + "status": "running", + "input": { "description": "Shows working tree status", "command": "git status" }, + "time": { "start": 1767622076189 } + }, + "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx", + "tool": "bash" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "tool": { "messageID": "msg_b8e7c60f1001aEWYlAaDRXQ4aJ", "callID": "call_KAC0LdAxKEbU6HTAPH5QJfGx" }, + "patterns": ["git status"], + "id": "per_b8e7c6744001DX6EZ9KHM15RLi", + "permission": "bash", + "always": ["git status*"], + "metadata": {} + }, + "type": "permission.asked" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "sessionID": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "created": 1767622074530 }, + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "role": "user", + "id": "msg_b8e7c60a2001Kisjwk2mVB4dye", + "agent": "build", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "summary": { "title": "Running git status to list changed files", "diffs": [] } + } + }, + "type": "message.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" }, + { + "properties": { + "info": { + "title": "Running git status for formatter.lua changes", + "version": "1.1.2", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_47183a8abffeZzOLeJ3j2KV8f8", + "time": { "updated": 1767622082619, "created": 1767622072149 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + }, + "type": "session.updated" + }, + { "properties": [], "type": "custom.emit_events.finished" } +] diff --git a/tests/data/permission-prompt.expected.json b/tests/data/permission-prompt.expected.json index c4a91d6b..46b91edd 100644 --- a/tests/data/permission-prompt.expected.json +++ b/tests/data/permission-prompt.expected.json @@ -1 +1,319 @@ -{"lines":["----","","","Perfect! Now I understand how it works. The message headers have extmarks with `virt_text` where the first element contains the icon (either `header_user` or `header_assistant`). Let me check the output_window module to understand the extmark namespace:","","** run** `Find extmark namespace usage`","","`````bash","> rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2","","`````","","----","",""," Permission Required",""," *bash* `rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2`",""," 1. Allow once "," 2. Reject"," 3. Allow always","","Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`","",""],"extmarks":[[1,1,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-16 04:27:36)","OpencodeHint"],[" [msg_9eb45fbe60020xE560OGH3Vdoo]","OpencodeHint"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":10,"virt_text_repeat_linebreak":false,"virt_text_win_col":-3}],[2,5,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[3,6,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[4,7,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[5,8,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[6,9,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[7,10,0,{"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-1}],[8,13,0,{"virt_text":[[" ","OpencodeMessageRoleSystem"],[" "],["SYSTEM","OpencodeMessageRoleSystem"],["","OpencodeHint"],["","OpencodeHint"],[" [permission-display-message]","OpencodeHint"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":10,"virt_text_repeat_linebreak":false,"virt_text_win_col":-3}],[9,15,0,{"priority":4096,"line_hl_group":"OpencodePermissionTitle","ns_id":3,"right_gravity":true}],[10,15,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[11,16,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[12,17,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[13,18,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[14,19,0,{"priority":4096,"line_hl_group":"OpencodeDialogOptionHover","ns_id":3,"right_gravity":true}],[15,19,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[16,19,2,{"virt_text":[["› ","OpencodeDialogOptionHover"]],"virt_text_pos":"overlay","ns_id":3,"right_gravity":true,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"priority":4096}],[17,20,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[18,21,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[19,22,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}],[20,23,0,{"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","ns_id":3,"right_gravity":true,"virt_text_hide":false,"priority":4096,"virt_text_repeat_linebreak":true,"virt_text_win_col":-2}]],"timestamp":1769449859,"actions":[]} \ No newline at end of file +{ + "lines": [ + "----", + "", + "", + "Perfect! Now I understand how it works. The message headers have extmarks with `virt_text` where the first element contains the icon (either `header_user` or `header_assistant`). Let me check the output_window module to understand the extmark namespace:", + "", + "** run** `Find extmark namespace usage`", + "", + "`````bash", + "> rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2", + "", + "`````", + "", + "----", + "", + "", + " Permission Required", + "", + " *bash* `rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2`", + "", + " 1. Allow once ", + " 2. Reject", + " 3. Allow always", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-16 04:27:36)", "OpencodeHint"], + [" [msg_9eb45fbe60020xE560OGH3Vdoo]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 10, + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ], + [ + 2, + 5, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 3, + 6, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 4, + 7, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 5, + 8, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 6, + 9, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 7, + 10, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 + } + ], + [ + 8, + 13, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleSystem"], + [" "], + ["SYSTEM", "OpencodeMessageRoleSystem"], + ["", "OpencodeHint"], + ["", "OpencodeHint"], + [" [permission-display-message]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 10, + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ], + [9, 15, 0, { "priority": 4096, "line_hl_group": "OpencodePermissionTitle", "ns_id": 3, "right_gravity": true }], + [ + 10, + 15, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 11, + 16, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 12, + 17, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 13, + 18, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [14, 19, 0, { "priority": 4096, "line_hl_group": "OpencodeDialogOptionHover", "ns_id": 3, "right_gravity": true }], + [ + 15, + 19, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 16, + 19, + 2, + { + "virt_text": [["› ", "OpencodeDialogOptionHover"]], + "virt_text_pos": "overlay", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "priority": 4096 + } + ], + [ + 17, + 20, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 18, + 21, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 19, + 22, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 20, + 23, + 0, + { + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "ns_id": 3, + "right_gravity": true, + "virt_text_hide": false, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ] + ], + "timestamp": 1769449859, + "actions": [] +} diff --git a/tests/data/permission.expected.json b/tests/data/permission.expected.json index 176d38fd..5155be7f 100644 --- a/tests/data/permission.expected.json +++ b/tests/data/permission.expected.json @@ -1 +1,224 @@ -{"extmarks":[[1,1,0,{"ns_id":3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-12 05:43:49)","OpencodeHint"],[" [msg_9d6f253910015UFmkGkiWtUsRW]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}],[2,2,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[3,3,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":true}],[4,6,0,{"ns_id":3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-12 05:43:49)","OpencodeHint"],[" [msg_9d6f253df001TjqxW12FAjGf5s]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}],[5,8,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[6,9,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[7,10,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[8,11,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[9,12,0,{"ns_id":3,"priority":4096,"virt_text_hide":false,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"virt_text_repeat_linebreak":true}],[10,17,0,{"ns_id":3,"priority":10,"virt_text_hide":false,"right_gravity":true,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-12 05:44:00)","OpencodeHint"],[" [msg_9d6f27f4800103Tp3N6i6JW53p]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"virt_text_repeat_linebreak":false}]],"actions":[{"key":"R","type":"diff_revert_selected_file","text":"[R]evert file","args":["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"],"range":{"from":14,"to":14},"display_line":14},{"key":"A","type":"diff_revert_all","text":"Revert [A]ll","args":["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"],"range":{"from":14,"to":14},"display_line":14},{"key":"D","type":"diff_open","text":"[D]iff","args":["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"],"range":{"from":14,"to":14},"display_line":14}],"lines":["----","","","add a file, test.txt, with \":)\" in it","","----","","","** write** `/Users/cam/tmp/a/test.txt`","","`````txt",":)","`````","","**󰻛 Created Snapshot** `c78fb2dd`","","----","","",""],"timestamp":1769172178} \ No newline at end of file +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-12 05:43:49)", "OpencodeHint"], + [" [msg_9d6f253910015UFmkGkiWtUsRW]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 6, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-12 05:43:49)", "OpencodeHint"], + [" [msg_9d6f253df001TjqxW12FAjGf5s]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ], + [ + 5, + 8, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 9, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 10, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 8, + 11, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 12, + 0, + { + "ns_id": 3, + "priority": 4096, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 17, + 0, + { + "ns_id": 3, + "priority": 10, + "virt_text_hide": false, + "right_gravity": true, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-12 05:44:00)", "OpencodeHint"], + [" [msg_9d6f27f4800103Tp3N6i6JW53p]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false + } + ] + ], + "actions": [ + { + "key": "R", + "type": "diff_revert_selected_file", + "text": "[R]evert file", + "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], + "range": { "from": 14, "to": 14 }, + "display_line": 14 + }, + { + "key": "A", + "type": "diff_revert_all", + "text": "Revert [A]ll", + "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], + "range": { "from": 14, "to": 14 }, + "display_line": 14 + }, + { + "key": "D", + "type": "diff_open", + "text": "[D]iff", + "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], + "range": { "from": 14, "to": 14 }, + "display_line": 14 + } + ], + "lines": [ + "----", + "", + "", + "add a file, test.txt, with \":)\" in it", + "", + "----", + "", + "", + "** write** `/Users/cam/tmp/a/test.txt`", + "", + "`````txt", + ":)", + "`````", + "", + "**󰻛 Created Snapshot** `c78fb2dd`", + "", + "----", + "", + "", + "" + ], + "timestamp": 1769172178 +} diff --git a/tests/data/permission_error.json b/tests/data/permission_error.json index 541ec3ca..9286166b 100644 --- a/tests/data/permission_error.json +++ b/tests/data/permission_error.json @@ -1 +1,1677 @@ -[{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","agent":"plan","id":"msg_b94b69474001IGYFIs72LkX50a","time":{"created":1767726552181},"model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"message.part.updated","properties":{"part":{"type":"agent","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","messageID":"msg_b94b69474001IGYFIs72LkX50a","id":"prt_b94b69475001ahJ6Yfg4gUhw5v","source":{"end":5,"value":"@nvim","start":0},"name":"nvim"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Use the above message and context to generate a prompt and call the task tool with subagent: nvim","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","synthetic":true,"id":"prt_b94b69475002LiAJbv6BRaVSpQ","messageID":"msg_b94b69474001IGYFIs72LkX50a"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua\"}","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","synthetic":true,"id":"prt_b94b69475004x7HkswYBcQhl62","messageID":"msg_b94b69474001IGYFIs72LkX50a"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"\n00001| return {\n00002| \"sudo-tee/opencode.nvim\",\n00003| config = function()\n00004| -- Check if custom configs are enabled\n00005| local custom_enabled = vim.g.opencode_custom_enabled == true\n00006| \n00007| if custom_enabled then\n00008| -- =========================================================================\n00009| -- CUSTOM CONFIGURATION MODE\n00010| -- =========================================================================\n00011| require(\"opencode\").setup({\n00012| default_mode = \"plan\", -- Default to Plan mode instead of Build\n00013| keymap = {\n00014| editor = {\n00015| [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n00016| [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n00017| [\"o\"] = { \"redo\", desc = \"Redo message\" },\n00018| -- Override default provider switcher with custom favorites\n00019| [\"op\"] = {\n00020| function()\n00021| require(\"gmati.plugins.ai.opencode_favorites\").select_favorite_provider()\n00022| end,\n00023| desc = \"Switch provider (favorites)\",\n00024| },\n00025| },\n00026| input_window = {\n00027| -- Enter submits only in normal mode\n00028| [\"\"] = { \"submit_input_prompt\", mode = \"n\" },\n00029| },\n00030| },\n00031| ui = {\n00032| -- Disable built-in status displays (we use lualine instead)\n00033| display_model = false,\n00034| display_context_size = false,\n00035| display_cost = false,\n00036| },\n00037| debug = {\n00038| capture_streamed_events = true\n00039| }\n00040| })\n00041| \n00042| -- Apply UI patches to customize OpenCode rendering\n00043| vim.defer_fn(function()\n00044| require(\"gmati.plugins.ai.opencode_ui_patches\").apply_all()\n00045| end, 0)\n00046| \n00047| -- Setup custom keymaps (mode switching, diagnostics, emergency)\n00048| require(\"gmati.plugins.ai.opencode_keymaps\").setup()\n00049| \n00050| -- Setup diagnostics monitoring (health checks, error tracking)\n00051| vim.defer_fn(function()\n00052| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00053| if ok then\n00054| diagnostics.setup()\n00055| end\n00056| end, 300)\n00057| \n00058| -- Setup LSP/features integration (disable during thinking)\n00059| vim.defer_fn(function()\n00060| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00061| if ok then\n00062| integration.setup()\n00063| end\n00064| end, 200)\n00065| \n00066| -- Cleanup on exit\n00067| vim.api.nvim_create_autocmd(\"VimLeavePre\", {\n00068| callback = function()\n00069| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00070| if ok then\n00071| integration.cleanup()\n00072| end\n00073| \n00074| local ok_diag, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00075| if ok_diag then\n00076| diagnostics.cleanup()\n00077| end\n00078| end,\n00079| })\n00080| else\n00081| -- =========================================================================\n00082| -- VANILLA CONFIGURATION MODE\n00083| -- =========================================================================\n00084| require(\"opencode\").setup({\n00085| default_mode = \"plan\", -- Keep your default mode preference\n00086| debug = {\n00087| capture_streamed_events = true\n00088| }\n00089| })\n00090| end\n00091| end,\n00092| dependencies = {\n00093| \"nvim-lua/plenary.nvim\",\n00094| {\n00095| \"MeanderingProgrammer/render-markdown.nvim\",\n00096| opts = {\n00097| anti_conceal = { enabled = false },\n00098| file_types = { \"markdown\", \"opencode_output\" },\n00099| },\n00100| ft = { \"markdown\", \"Avante\", \"copilot-chat\", \"opencode_output\" },\n00101| },\n00102| -- Optional, for file mentions and commands completion\n00103| \"hrsh7th/nvim-cmp\",\n00104| -- Optional, for file mentions picker\n00105| \"folke/snacks.nvim\",\n00106| },\n00107| }\n00108| \n\n(End of file - total 108 lines)\n","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","synthetic":true,"id":"prt_b94b69478001tl1GxEgWIuCfyD","messageID":"msg_b94b69474001IGYFIs72LkX50a"}}},{"type":"message.part.updated","properties":{"part":{"type":"file","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","url":"file:///Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua","filename":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua","id":"prt_b94b694780020D1il48agKCnJG","messageID":"msg_b94b69474001IGYFIs72LkX50a","mime":"text/plain"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"@nvim my alt-M keybind does not work for opencode.nvim plugin for nvim, but it is the default keymap of the plugin\n","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b694750033Bhav31Nb5Zl3j","messageID":"msg_b94b69474001IGYFIs72LkX50a"}}},{"type":"session.updated","properties":{"info":{"title":"New session - 2026-01-06T19:08:58.999Z","version":"1.0.220","directory":"/Users/matigreen/dotfiles","id":"ses_46b499f08ffe3aldWmWZoH6N2m","time":{"created":1767726538999,"updated":1767726552190},"projectID":"b8136e83cf077521b7177c9660d223a1759f881e"}}},{"type":"message.updated","properties":{"info":{"role":"assistant","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_b94b69474001IGYFIs72LkX50a","providerID":"anthropic","time":{"created":1767726552205},"id":"msg_b94b6948d002pFurK7t7q4bSyb","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","modelID":"claude-sonnet-4-5","mode":"plan","cost":0,"agent":"plan","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"title":"Debugging Alt-M keybind in opencode.nvim","version":"1.0.220","directory":"/Users/matigreen/dotfiles","id":"ses_46b499f08ffe3aldWmWZoH6N2m","time":{"created":1767726538999,"updated":1767726553777},"projectID":"b8136e83cf077521b7177c9660d223a1759f881e"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"diffs":[]},"sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","time":{"created":1767726552181},"id":"msg_b94b69474001IGYFIs72LkX50a","agent":"plan","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"session.updated","properties":{"info":{"title":"Debugging Alt-M keybind in opencode.nvim","version":"1.0.220","projectID":"b8136e83cf077521b7177c9660d223a1759f881e","directory":"/Users/matigreen/dotfiles","id":"ses_46b499f08ffe3aldWmWZoH6N2m","summary":{"deletions":0,"files":0,"additions":0},"time":{"created":1767726538999,"updated":1767726554586}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"title":"Debugging alt-M keybind in opencode.nvim","diffs":[]},"sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","system":"# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n","time":{"created":1767726552181},"id":"msg_b94b69474001IGYFIs72LkX50a","agent":"plan","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0ad0015WllkaFXZte33S","messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"message.part.updated","properties":{"delta":"I'll","part":{"type":"text","text":"I'll","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" the","part":{"type":"text","text":"I'll help you investigate why the","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"-","part":{"type":"text","text":"I'll help you investigate why the `alt-","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"M","part":{"type":"text","text":"I'll help you investigate why the `alt-M","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"` ke","part":{"type":"text","text":"I'll help you investigate why the `alt-M` ke","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"ybin","part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybin","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" open","part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybind isn't working for the open","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"code.","part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybind isn't working for the opencode.","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" agent","part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":".","part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent.","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726559407},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent.","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","id":"prt_b94b6b0af001kAm5cXAFC8lXrL","time":{"start":1767726560755,"end":1767726560755},"messageID":"msg_b94b6948d002pFurK7t7q4bSyb"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"title":"Debug alt-M keybind issue (@nvim subagent)","version":"1.0.220","projectID":"b8136e83cf077521b7177c9660d223a1759f881e","directory":"/Users/matigreen/dotfiles","parentID":"ses_46b499f08ffe3aldWmWZoH6N2m","time":{"created":1767726567748,"updated":1767726567748},"id":"ses_46b492ebbffel5oIJ1qUl9GKFh"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726567751},"status":"running"}}}},{"type":"message.updated","properties":{"info":{"role":"user","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","tools":{"todoread":false,"task":false,"browser_*":false,"atlassian_*":false,"jira_get":true,"confluence_*":false,"todowrite":false,"playwright":false,"write":true,"edit":true,"bash":true},"agent":"nvim","id":"msg_b94b6d147001k2DbeF1m2JpIP3","time":{"created":1767726567752},"model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6d148001ScODvsxRwAPkK7","messageID":"msg_b94b6d147001k2DbeF1m2JpIP3"}}},{"type":"session.updated","properties":{"info":{"title":"Debug alt-M keybind issue (@nvim subagent)","version":"1.0.220","projectID":"b8136e83cf077521b7177c9660d223a1759f881e","directory":"/Users/matigreen/dotfiles","parentID":"ses_46b499f08ffe3aldWmWZoH6N2m","time":{"created":1767726567748,"updated":1767726567754},"id":"ses_46b492ebbffel5oIJ1qUl9GKFh"}}},{"type":"message.updated","properties":{"info":{"role":"assistant","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726567755},"id":"msg_b94b6d14b001y9ggcfX7Qmvfb4","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"diffs":[]},"sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","tools":{"todoread":false,"task":false,"browser_*":false,"atlassian_*":false,"jira_get":true,"confluence_*":false,"todowrite":false,"playwright":false,"write":true,"edit":true,"bash":true},"time":{"created":1767726567752},"id":"msg_b94b6d147001k2DbeF1m2JpIP3","agent":"nvim","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"session.updated","properties":{"info":{"projectID":"b8136e83cf077521b7177c9660d223a1759f881e","title":"Debug alt-M keybind issue (@nvim subagent)","version":"1.0.220","time":{"created":1767726567748,"updated":1767726567766},"directory":"/Users/matigreen/dotfiles","parentID":"ses_46b499f08ffe3aldWmWZoH6N2m","summary":{"deletions":0,"files":0,"additions":0},"id":"ses_46b492ebbffel5oIJ1qUl9GKFh"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"title":"Debugging alt-M keybind opencode.nvim","diffs":[]},"sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","tools":{"todoread":false,"task":false,"browser_*":false,"atlassian_*":false,"jira_get":true,"confluence_*":false,"todowrite":false,"playwright":false,"write":true,"edit":true,"bash":true},"time":{"created":1767726567752},"id":"msg_b94b6d147001k2DbeF1m2JpIP3","agent":"nvim","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6de23001y1RTIdtsaHvsAw","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4"}}},{"type":"message.part.updated","properties":{"delta":" the `","part":{"type":"text","text":"I'll investigate the `","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6de240010A9XWmezoH5cz9","time":{"start":1767726571044},"messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"code.nvim.","part":{"type":"text","text":"I'll investigate the `alt-M` keybind issue for opencode.nvim.","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6de240010A9XWmezoH5cz9","time":{"start":1767726571044},"messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" files","part":{"type":"text","text":"I'll investigate the `alt-M` keybind issue for opencode.nvim. Let me start by examining the relevant configuration files","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6de240010A9XWmezoH5cz9","time":{"start":1767726571044},"messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"I'll investigate the `alt-M` keybind issue for opencode.nvim. Let me start by examining the relevant configuration files.","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6de240010A9XWmezoH5cz9","time":{"start":1767726571695,"end":1767726571695},"messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01BbJ53KgPXPLXqVbMQhaPyT","tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"pending"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726571700},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01BbJ53KgPXPLXqVbMQhaPyT","tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"status":"completed","output":"\n00001| return {\n00002| \"sudo-tee/opencode.nvim\",\n00003| config = function()\n00004| -- Check if custom configs are enabled\n00005| local custom_enabled = vim.g.opencode_custom_enabled == true\n00006| \n00007| if custom_enabled then\n00008| -- =========================================================================\n00009| -- CUSTOM CONFIGURATION MODE\n00010| -- =========================================================================\n00011| require(\"opencode\").setup({\n00012| default_mode = \"plan\", -- Default to Plan mode instead of Build\n00013| keymap = {\n00014| editor = {\n00015| [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n00016| [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n00017| [\"o\"] = { \"redo\", desc = \"Redo message\" },\n00018| -- Override default provider switcher with custom favorites\n00019| [\"op\"] = {\n00020| function()\n00021| require(\"gmati.plugins.ai.opencode_favorites\").select_favorite_provider()\n00022| end,\n00023| desc = \"Switch provider (favorites)\",\n00024| },\n00025| },\n00026| input_window = {\n00027| -- Enter submits only in normal mode\n00028| [\"\"] = { \"submit_input_prompt\", mode = \"n\" },\n00029| },\n00030| },\n00031| ui = {\n00032| -- Disable built-in status displays (we use lualine instead)\n00033| display_model = false,\n00034| display_context_size = false,\n00035| display_cost = false,\n00036| },\n00037| debug = {\n00038| capture_streamed_events = true\n00039| }\n00040| })\n00041| \n00042| -- Apply UI patches to customize OpenCode rendering\n00043| vim.defer_fn(function()\n00044| require(\"gmati.plugins.ai.opencode_ui_patches\").apply_all()\n00045| end, 0)\n00046| \n00047| -- Setup custom keymaps (mode switching, diagnostics, emergency)\n00048| require(\"gmati.plugins.ai.opencode_keymaps\").setup()\n00049| \n00050| -- Setup diagnostics monitoring (health checks, error tracking)\n00051| vim.defer_fn(function()\n00052| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00053| if ok then\n00054| diagnostics.setup()\n00055| end\n00056| end, 300)\n00057| \n00058| -- Setup LSP/features integration (disable during thinking)\n00059| vim.defer_fn(function()\n00060| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00061| if ok then\n00062| integration.setup()\n00063| end\n00064| end, 200)\n00065| \n00066| -- Cleanup on exit\n00067| vim.api.nvim_create_autocmd(\"VimLeavePre\", {\n00068| callback = function()\n00069| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00070| if ok then\n00071| integration.cleanup()\n00072| end\n00073| \n00074| local ok_diag, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00075| if ok_diag then\n00076| diagnostics.cleanup()\n00077| end\n00078| end,\n00079| })\n00080| else\n00081| -- =========================================================================\n00082| -- VANILLA CONFIGURATION MODE\n00083| -- =========================================================================\n00084| require(\"opencode\").setup({\n00085| default_mode = \"plan\", -- Keep your default mode preference\n00086| debug = {\n00087| capture_streamed_events = true\n00088| }\n00089| })\n00090| end\n00091| end,\n00092| dependencies = {\n00093| \"nvim-lua/plenary.nvim\",\n00094| {\n00095| \"MeanderingProgrammer/render-markdown.nvim\",\n00096| opts = {\n00097| anti_conceal = { enabled = false },\n00098| file_types = { \"markdown\", \"opencode_output\" },\n00099| },\n00100| ft = { \"markdown\", \"Avante\", \"copilot-chat\", \"opencode_output\" },\n00101| },\n00102| -- Optional, for file mentions and commands completion\n00103| \"hrsh7th/nvim-cmp\",\n00104| -- Optional, for file mentions picker\n00105| \"folke/snacks.nvim\",\n00106| },\n00107| }\n00108| \n\n(End of file - total 108 lines)\n","metadata":{"preview":"return {\n \"sudo-tee/opencode.nvim\",\n config = function()\n -- Check if custom configs are enabled\n local custom_enabled = vim.g.opencode_custom_enabled == true\n\n if custom_enabled then\n -- =========================================================================\n -- CUSTOM CONFIGURATION MODE\n -- =========================================================================\n require(\"opencode\").setup({\n default_mode = \"plan\", -- Default to Plan mode instead of Build\n keymap = {\n editor = {\n [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n [\"o\"] = { \"redo\", desc = \"Redo message\" },\n -- Override default provider switcher with custom favorites\n [\"op\"] = {\n function()"},"input":{"filePath":"/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"},"time":{"start":1767726572684,"end":1767726572708},"title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"pending"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726572710},"status":"running"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01NLzNf8fQ5m6CbQmJi61LHi","tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01NLzNf8fQ5m6CbQmJi61LHi","tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"status":"completed","output":"\n00001| -- OpenCode Keymaps\n00002| -- All custom keybindings for OpenCode functionality\n00003| -- Organized by category for easy discovery and modification\n00004| \n00005| local M = {}\n00006| \n00007| -- Setup all OpenCode keymaps\n00008| function M.setup()\n00009| M.setup_mode_keymaps()\n00010| M.setup_diagnostic_keymaps()\n00011| M.setup_emergency_keymaps()\n00012| end\n00013| \n00014| -- Mode-related keymaps (switching modes, quick build)\n00015| function M.setup_mode_keymaps()\n00016| -- Quick build mode: Switch to build mode and prepare \"build\" command\n00017| vim.keymap.set(\"n\", \"o\", function()\n00018| require(\"gmati.plugins.ai.opencode_build_mode\").quick_build()\n00019| end, { desc = \"Build mode + prepare 'build'\" })\n00020| end\n00021| \n00022| -- Diagnostic keymaps (health status, logs)\n00023| function M.setup_diagnostic_keymaps()\n00024| -- Show OpenCode diagnostics (health status)\n00025| vim.keymap.set(\"n\", \"od\", function()\n00026| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00027| if ok then\n00028| diagnostics.show_health_status()\n00029| else\n00030| vim.notify(\"Failed to load OpenCode diagnostics\", vim.log.levels.ERROR)\n00031| end\n00032| end, { desc = \"Show OpenCode diagnostics\" })\n00033| \n00034| -- Open OpenCode server logs\n00035| vim.keymap.set(\"n\", \"oL\", function()\n00036| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00037| if ok then\n00038| diagnostics.open_server_logs()\n00039| else\n00040| vim.notify(\"Failed to load OpenCode diagnostics\", vim.log.levels.ERROR)\n00041| end\n00042| end, { desc = \"View OpenCode server logs\" })\n00043| end\n00044| \n00045| -- Emergency keymaps (force stop)\n00046| function M.setup_emergency_keymaps()\n00047| -- Force stop OpenCode: Nuclear option to kill all jobs and reset state\n00048| vim.keymap.set(\"n\", \"oF\", function()\n00049| require(\"gmati.plugins.ai.opencode_force_stop\").force_stop()\n00050| end, { desc = \"Force stop OpenCode (nuclear option)\" })\n00051| end\n00052| \n00053| return M\n00054| \n\n(End of file - total 54 lines)\n","metadata":{"preview":"-- OpenCode Keymaps\n-- All custom keybindings for OpenCode functionality\n-- Organized by category for easy discovery and modification\n\nlocal M = {}\n\n-- Setup all OpenCode keymaps\nfunction M.setup()\n M.setup_mode_keymaps()\n M.setup_diagnostic_keymaps()\n M.setup_emergency_keymaps()\nend\n\n-- Mode-related keymaps (switching modes, quick build)\nfunction M.setup_mode_keymaps()\n -- Quick build mode: Switch to build mode and prepare \"build\" command\n vim.keymap.set(\"n\", \"o\", function()\n require(\"gmati.plugins.ai.opencode_build_mode\").quick_build()\n end, { desc = \"Build mode + prepare 'build'\" })\nend"},"input":{"filePath":"/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"},"time":{"start":1767726573388,"end":1767726573393},"title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"pending"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726573417},"status":"running"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01SkSniBkbNnBeJr1jwfEvvs","tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01SkSniBkbNnBeJr1jwfEvvs","tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","state":{"status":"completed","output":"\n00001| initial-window = false\n00002| \n00003| quick-terminal-position = center\n00004| quick-terminal-size = 98%,95%\n00005| \n00006| window-padding-x = 16\n00007| window-padding-y = 16\n00008| window-padding-balance = true\n00009| \n00010| theme = catppuccin-mocha.conf\n00011| background-opacity = 0.9\n00012| background-blur-radius = 20\n00013| \n00014| font-family = \"Hack Nerd Font\"\n00015| font-size = 18\n00016| \n00017| # Enable Option key as Alt modifier for terminal keybindings\n00018| macos-option-as-alt = true\n00019| \n00020| keybind = global:cmd+f12=toggle_quick_terminal\n00021| \n00022| keybind = cmd+shift+k=goto_split:top\n00023| keybind = cmd+shift+j=goto_split:bottom\n00024| keybind = cmd+shift+h=goto_split:left\n00025| keybind = cmd+shift+l=goto_split:right\n00026| \n\n(End of file - total 26 lines)\n","metadata":{"preview":"initial-window = false\n\nquick-terminal-position = center\nquick-terminal-size = 98%,95%\n\nwindow-padding-x = 16\nwindow-padding-y = 16\nwindow-padding-balance = true\n\ntheme = catppuccin-mocha.conf\nbackground-opacity = 0.9\nbackground-blur-radius = 20\n\nfont-family = \"Hack Nerd Font\"\nfont-size = 18\n\n# Enable Option key as Alt modifier for terminal keybindings\nmacos-option-as-alt = true\n\nkeybind = global:cmd+f12=toggle_quick_terminal"},"input":{"filePath":"/Users/matigreen/dotfiles/ghostty/.config/ghostty/config"},"time":{"start":1767726573681,"end":1767726573695},"title":"ghostty/.config/ghostty/config"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726573696},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-finish","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","reason":"tool-calls","messageID":"msg_b94b6d14b001y9ggcfX7Qmvfb4","id":"prt_b94b6e910001sHfwoYXshpCwWv","cost":0,"tokens":{"reasoning":0,"input":2,"output":235,"cache":{"write":24673,"read":0}}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","finish":"tool-calls","tokens":{"reasoning":0,"input":2,"output":235,"cache":{"write":24673,"read":0}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726567755},"id":"msg_b94b6d14b001y9ggcfX7Qmvfb4","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","finish":"tool-calls","tokens":{"reasoning":0,"input":2,"output":235,"cache":{"write":24673,"read":0}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726567755,"completed":1767726573914},"id":"msg_b94b6d14b001y9ggcfX7Qmvfb4","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726573918},"id":"msg_b94b6e95e001xIoTqqCWgaqBfO","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"title":"Debugging alt-M keybind opencode.nvim","diffs":[]},"sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","tools":{"todoread":false,"task":false,"browser_*":false,"atlassian_*":false,"jira_get":true,"confluence_*":false,"todowrite":false,"playwright":false,"write":true,"edit":true,"bash":true},"time":{"created":1767726567752},"id":"msg_b94b6d147001k2DbeF1m2JpIP3","agent":"nvim","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"session.updated","properties":{"info":{"projectID":"b8136e83cf077521b7177c9660d223a1759f881e","title":"Debug alt-M keybind issue (@nvim subagent)","version":"1.0.220","time":{"created":1767726567748,"updated":1767726573935},"directory":"/Users/matigreen/dotfiles","parentID":"ses_46b499f08ffe3aldWmWZoH6N2m","summary":{"deletions":0,"files":0,"additions":0},"id":"ses_46b492ebbffel5oIJ1qUl9GKFh"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f600001vrOB25LzH46DAo","messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"message.part.updated","properties":{"delta":" let","part":{"type":"text","text":"Now let","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"code.","part":{"type":"text","text":"Now let me check the opencode.","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" or","part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":" default","part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or source to understand what the default","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"alt","part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"` ke","part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` ke","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"delta":"d does","part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` keybind does","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726577153},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` keybind does:","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b6f601001imJME5kdm4mlg6","time":{"start":1767726578336,"end":1767726578336},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_0135HE3hqPJhMmqYtvzz2xyu","tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}},{"tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","state":{"status":"pending"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726578336},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_0135HE3hqPJhMmqYtvzz2xyu","tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO","state":{"input":{"command":"cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20","description":"Find opencode.nvim plugin files"},"time":{"start":1767726579322},"status":"running"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}},{"tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","state":{"status":"running"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726579323},"status":"running"}}}},{"type":"permission.updated","properties":{"title":"cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20","pattern":["find . *","head *"],"type":"bash","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_0135HE3hqPJhMmqYtvzz2xyu","metadata":{"command":"cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20","patterns":["find . *","head *"]},"id":"per_b94b6fe95001R3s3aeqFb9wsLe","time":{"created":1767726579349},"messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"permission.replied","properties":{"permissionID":"per_b94b6fe95001R3s3aeqFb9wsLe","response":"once","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh"}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_0135HE3hqPJhMmqYtvzz2xyu","tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO","state":{"status":"completed","output":"./tests/mocks/session_list.lua\n./tests/unit/hooks_spec.lua\n./tests/unit/timer_spec.lua\n./tests/unit/input_window_spec.lua\n./tests/unit/server_job_spec.lua\n./tests/unit/config_spec.lua\n./tests/unit/api_spec.lua\n./tests/unit/event_manager_spec.lua\n./tests/unit/init_spec.lua\n./tests/unit/state_spec.lua\n./tests/unit/image_handler_spec.lua\n./tests/unit/id_spec.lua\n./tests/unit/api_client_spec.lua\n./tests/unit/context_spec.lua\n./tests/unit/session_spec.lua\n./tests/unit/config_file_spec.lua\n./tests/unit/opencode_server_spec.lua\n./tests/unit/core_spec.lua\n./tests/unit/context_completion_spec.lua\n./tests/unit/render_state_spec.lua\n","metadata":{"exit":0,"output":"./tests/mocks/session_list.lua\n./tests/unit/hooks_spec.lua\n./tests/unit/timer_spec.lua\n./tests/unit/input_window_spec.lua\n./tests/unit/server_job_spec.lua\n./tests/unit/config_spec.lua\n./tests/unit/api_spec.lua\n./tests/unit/event_manager_spec.lua\n./tests/unit/init_spec.lua\n./tests/unit/state_spec.lua\n./tests/unit/image_handler_spec.lua\n./tests/unit/id_spec.lua\n./tests/unit/api_client_spec.lua\n./tests/unit/context_spec.lua\n./tests/unit/session_spec.lua\n./tests/unit/config_file_spec.lua\n./tests/unit/opencode_server_spec.lua\n./tests/unit/core_spec.lua\n./tests/unit/context_completion_spec.lua\n./tests/unit/render_state_spec.lua\n","description":"Find opencode.nvim plugin files"},"input":{"command":"cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20","description":"Find opencode.nvim plugin files"},"time":{"start":1767726579322,"end":1767726603966},"title":"Find opencode.nvim plugin files"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}},{"tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","state":{"status":"completed","title":"Find opencode.nvim plugin files"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726603967},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-finish","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","reason":"tool-calls","messageID":"msg_b94b6e95e001xIoTqqCWgaqBfO","id":"prt_b94b75ec0001Fsf2UjpQApsRHh","cost":0,"tokens":{"reasoning":0,"input":6,"output":136,"cache":{"write":2712,"read":24673}}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","finish":"tool-calls","tokens":{"reasoning":0,"input":6,"output":136,"cache":{"write":2712,"read":24673}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726573918},"id":"msg_b94b6e95e001xIoTqqCWgaqBfO","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","finish":"tool-calls","tokens":{"reasoning":0,"input":6,"output":136,"cache":{"write":2712,"read":24673}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726573918,"completed":1767726604035},"id":"msg_b94b6e95e001xIoTqqCWgaqBfO","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"message.updated","properties":{"info":{"role":"assistant","tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"parentID":"msg_b94b6d147001k2DbeF1m2JpIP3","providerID":"anthropic","time":{"created":1767726604039},"id":"msg_b94b75f07001FvPfzM3IrqRHw3","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","modelID":"claude-sonnet-4-5","mode":"nvim","cost":0,"agent":"nvim","path":{"cwd":"/Users/matigreen/dotfiles","root":"/Users/matigreen/dotfiles"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"projectID":"b8136e83cf077521b7177c9660d223a1759f881e","title":"Debug alt-M keybind issue (@nvim subagent)","version":"1.0.220","time":{"created":1767726567748,"updated":1767726604056},"directory":"/Users/matigreen/dotfiles","parentID":"ses_46b499f08ffe3aldWmWZoH6N2m","summary":{"deletions":0,"files":0,"additions":0},"id":"ses_46b492ebbffel5oIJ1qUl9GKFh"}}},{"type":"message.updated","properties":{"info":{"role":"user","summary":{"title":"Debugging alt-M keybind opencode.nvim","diffs":[]},"sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","tools":{"todoread":false,"task":false,"browser_*":false,"atlassian_*":false,"jira_get":true,"confluence_*":false,"todowrite":false,"playwright":false,"write":true,"edit":true,"bash":true},"time":{"created":1767726567752},"id":"msg_b94b6d147001k2DbeF1m2JpIP3","agent":"nvim","model":{"providerID":"anthropic","modelID":"claude-sonnet-4-5"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"29b76c5b84a59e892d23447cd82abb3afea86e41","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","id":"prt_b94b76a1d001uetcdCGKMa7pGM","messageID":"msg_b94b75f07001FvPfzM3IrqRHw3"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01XNzaXKr6tXjYSfRSikTw7Z","tool":"bash","id":"prt_b94b76a20001J4RYEL24y0dMrZ","messageID":"msg_b94b75f07001FvPfzM3IrqRHw3","state":{"input":{},"raw":"","status":"pending"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}},{"tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","state":{"status":"completed","title":"Find opencode.nvim plugin files"}},{"tool":"bash","id":"prt_b94b76a20001J4RYEL24y0dMrZ","state":{"status":"pending"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726606880},"status":"running"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01XNzaXKr6tXjYSfRSikTw7Z","tool":"bash","id":"prt_b94b76a20001J4RYEL24y0dMrZ","messageID":"msg_b94b75f07001FvPfzM3IrqRHw3","state":{"input":{"command":"cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20","description":"Search for alt-M keybind in opencode.nvim"},"time":{"start":1767726607982},"status":"running"}}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","sessionID":"ses_46b499f08ffe3aldWmWZoH6N2m","callID":"toolu_017Li9bzxEHcp7pwBxRu4MxU","tool":"task","id":"prt_b94b6b5f3001JaaXlpjO9TFn2O","messageID":"msg_b94b6948d002pFurK7t7q4bSyb","state":{"title":"Debug alt-M keybind issue","metadata":{"summary":[{"tool":"read","id":"prt_b94b6e0b3001mCVItg2EJfPAB2","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua"}},{"tool":"read","id":"prt_b94b6e4a1001n0heEU3dEGxfWa","state":{"status":"completed","title":"nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua"}},{"tool":"read","id":"prt_b94b6e768001nwQ5wgnzVRZL8B","state":{"status":"completed","title":"ghostty/.config/ghostty/config"}},{"tool":"bash","id":"prt_b94b6faa0001LKlGQTIabbHKvg","state":{"status":"completed","title":"Find opencode.nvim plugin files"}},{"tool":"bash","id":"prt_b94b76a20001J4RYEL24y0dMrZ","state":{"status":"running"}}],"sessionId":"ses_46b492ebbffel5oIJ1qUl9GKFh"},"input":{"subagent_type":"nvim","prompt":"The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.","description":"Debug alt-M keybind issue"},"time":{"start":1767726607982},"status":"running"}}}},{"type":"permission.updated","properties":{"title":"cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20","pattern":["grep \"alt-M\\|\" *","head *"],"type":"bash","sessionID":"ses_46b492ebbffel5oIJ1qUl9GKFh","callID":"toolu_01XNzaXKr6tXjYSfRSikTw7Z","metadata":{"command":"cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20","patterns":["grep \"alt-M\\|\" *","head *"]},"id":"per_b94b76e73001ml5seBvInCYYHG","time":{"created":1767726607987},"messageID":"msg_b94b75f07001FvPfzM3IrqRHw3"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]}] +[ + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "agent": "plan", + "id": "msg_b94b69474001IGYFIs72LkX50a", + "time": { "created": 1767726552181 }, + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "agent", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "messageID": "msg_b94b69474001IGYFIs72LkX50a", + "id": "prt_b94b69475001ahJ6Yfg4gUhw5v", + "source": { "end": 5, "value": "@nvim", "start": 0 }, + "name": "nvim" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Use the above message and context to generate a prompt and call the task tool with subagent: nvim", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "synthetic": true, + "id": "prt_b94b69475002LiAJbv6BRaVSpQ", + "messageID": "msg_b94b69474001IGYFIs72LkX50a" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua\"}", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "synthetic": true, + "id": "prt_b94b69475004x7HkswYBcQhl62", + "messageID": "msg_b94b69474001IGYFIs72LkX50a" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "\n00001| return {\n00002| \"sudo-tee/opencode.nvim\",\n00003| config = function()\n00004| -- Check if custom configs are enabled\n00005| local custom_enabled = vim.g.opencode_custom_enabled == true\n00006| \n00007| if custom_enabled then\n00008| -- =========================================================================\n00009| -- CUSTOM CONFIGURATION MODE\n00010| -- =========================================================================\n00011| require(\"opencode\").setup({\n00012| default_mode = \"plan\", -- Default to Plan mode instead of Build\n00013| keymap = {\n00014| editor = {\n00015| [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n00016| [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n00017| [\"o\"] = { \"redo\", desc = \"Redo message\" },\n00018| -- Override default provider switcher with custom favorites\n00019| [\"op\"] = {\n00020| function()\n00021| require(\"gmati.plugins.ai.opencode_favorites\").select_favorite_provider()\n00022| end,\n00023| desc = \"Switch provider (favorites)\",\n00024| },\n00025| },\n00026| input_window = {\n00027| -- Enter submits only in normal mode\n00028| [\"\"] = { \"submit_input_prompt\", mode = \"n\" },\n00029| },\n00030| },\n00031| ui = {\n00032| -- Disable built-in status displays (we use lualine instead)\n00033| display_model = false,\n00034| display_context_size = false,\n00035| display_cost = false,\n00036| },\n00037| debug = {\n00038| capture_streamed_events = true\n00039| }\n00040| })\n00041| \n00042| -- Apply UI patches to customize OpenCode rendering\n00043| vim.defer_fn(function()\n00044| require(\"gmati.plugins.ai.opencode_ui_patches\").apply_all()\n00045| end, 0)\n00046| \n00047| -- Setup custom keymaps (mode switching, diagnostics, emergency)\n00048| require(\"gmati.plugins.ai.opencode_keymaps\").setup()\n00049| \n00050| -- Setup diagnostics monitoring (health checks, error tracking)\n00051| vim.defer_fn(function()\n00052| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00053| if ok then\n00054| diagnostics.setup()\n00055| end\n00056| end, 300)\n00057| \n00058| -- Setup LSP/features integration (disable during thinking)\n00059| vim.defer_fn(function()\n00060| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00061| if ok then\n00062| integration.setup()\n00063| end\n00064| end, 200)\n00065| \n00066| -- Cleanup on exit\n00067| vim.api.nvim_create_autocmd(\"VimLeavePre\", {\n00068| callback = function()\n00069| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00070| if ok then\n00071| integration.cleanup()\n00072| end\n00073| \n00074| local ok_diag, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00075| if ok_diag then\n00076| diagnostics.cleanup()\n00077| end\n00078| end,\n00079| })\n00080| else\n00081| -- =========================================================================\n00082| -- VANILLA CONFIGURATION MODE\n00083| -- =========================================================================\n00084| require(\"opencode\").setup({\n00085| default_mode = \"plan\", -- Keep your default mode preference\n00086| debug = {\n00087| capture_streamed_events = true\n00088| }\n00089| })\n00090| end\n00091| end,\n00092| dependencies = {\n00093| \"nvim-lua/plenary.nvim\",\n00094| {\n00095| \"MeanderingProgrammer/render-markdown.nvim\",\n00096| opts = {\n00097| anti_conceal = { enabled = false },\n00098| file_types = { \"markdown\", \"opencode_output\" },\n00099| },\n00100| ft = { \"markdown\", \"Avante\", \"copilot-chat\", \"opencode_output\" },\n00101| },\n00102| -- Optional, for file mentions and commands completion\n00103| \"hrsh7th/nvim-cmp\",\n00104| -- Optional, for file mentions picker\n00105| \"folke/snacks.nvim\",\n00106| },\n00107| }\n00108| \n\n(End of file - total 108 lines)\n", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "synthetic": true, + "id": "prt_b94b69478001tl1GxEgWIuCfyD", + "messageID": "msg_b94b69474001IGYFIs72LkX50a" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "file", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "url": "file:///Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua", + "filename": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua", + "id": "prt_b94b694780020D1il48agKCnJG", + "messageID": "msg_b94b69474001IGYFIs72LkX50a", + "mime": "text/plain" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "@nvim my alt-M keybind does not work for opencode.nvim plugin for nvim, but it is the default keymap of the plugin\n", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b694750033Bhav31Nb5Zl3j", + "messageID": "msg_b94b69474001IGYFIs72LkX50a" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "New session - 2026-01-06T19:08:58.999Z", + "version": "1.0.220", + "directory": "/Users/matigreen/dotfiles", + "id": "ses_46b499f08ffe3aldWmWZoH6N2m", + "time": { "created": 1767726538999, "updated": 1767726552190 }, + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_b94b69474001IGYFIs72LkX50a", + "providerID": "anthropic", + "time": { "created": 1767726552205 }, + "id": "msg_b94b6948d002pFurK7t7q4bSyb", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "modelID": "claude-sonnet-4-5", + "mode": "plan", + "cost": 0, + "agent": "plan", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "Debugging Alt-M keybind in opencode.nvim", + "version": "1.0.220", + "directory": "/Users/matigreen/dotfiles", + "id": "ses_46b499f08ffe3aldWmWZoH6N2m", + "time": { "created": 1767726538999, "updated": 1767726553777 }, + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "diffs": [] }, + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "time": { "created": 1767726552181 }, + "id": "msg_b94b69474001IGYFIs72LkX50a", + "agent": "plan", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "Debugging Alt-M keybind in opencode.nvim", + "version": "1.0.220", + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "directory": "/Users/matigreen/dotfiles", + "id": "ses_46b499f08ffe3aldWmWZoH6N2m", + "summary": { "deletions": 0, "files": 0, "additions": 0 }, + "time": { "created": 1767726538999, "updated": 1767726554586 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "title": "Debugging alt-M keybind in opencode.nvim", "diffs": [] }, + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "system": "# Code References\n\n**CRITICAL: Always use the file:// URI scheme when referencing files in responses AND wrap them in backticks.**\n\nFormat: `file://path/to/file.lua`, `file://path/to/file.lua:42`, or `file://path/to/file.lua:42-50`\n\nExamples:\n- CORRECT: \"The error is in `file://src/services/process.ts:712`\"\n- INCORRECT: \"The error is in file://src/services/process.ts:712\"\n- INCORRECT: \"The error is in src/services/process.ts:712\"\n\nThis matches the file:// URI format that the reference picker already parses from your responses, enabling automatic navigation.\n", + "time": { "created": 1767726552181 }, + "id": "msg_b94b69474001IGYFIs72LkX50a", + "agent": "plan", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0ad0015WllkaFXZte33S", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "delta": "I'll", + "part": { + "type": "text", + "text": "I'll", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " the", + "part": { + "type": "text", + "text": "I'll help you investigate why the", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "-", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "M", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "` ke", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` ke", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "ybin", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybin", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " open", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybind isn't working for the open", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "code.", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybind isn't working for the opencode.", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " agent", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": ".", + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent.", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726559407 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "I'll help you investigate why the `alt-M` keybind isn't working for the opencode.nvim plugin. Let me delegate this to the nvim specialist agent.", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "id": "prt_b94b6b0af001kAm5cXAFC8lXrL", + "time": { "start": 1767726560755, "end": 1767726560755 }, + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "Debug alt-M keybind issue (@nvim subagent)", + "version": "1.0.220", + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "directory": "/Users/matigreen/dotfiles", + "parentID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "time": { "created": 1767726567748, "updated": 1767726567748 }, + "id": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726567751 }, + "status": "running" + } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "tools": { + "todoread": false, + "task": false, + "browser_*": false, + "atlassian_*": false, + "jira_get": true, + "confluence_*": false, + "todowrite": false, + "playwright": false, + "write": true, + "edit": true, + "bash": true + }, + "agent": "nvim", + "id": "msg_b94b6d147001k2DbeF1m2JpIP3", + "time": { "created": 1767726567752 }, + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6d148001ScODvsxRwAPkK7", + "messageID": "msg_b94b6d147001k2DbeF1m2JpIP3" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "title": "Debug alt-M keybind issue (@nvim subagent)", + "version": "1.0.220", + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "directory": "/Users/matigreen/dotfiles", + "parentID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "time": { "created": 1767726567748, "updated": 1767726567754 }, + "id": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726567755 }, + "id": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "diffs": [] }, + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "tools": { + "todoread": false, + "task": false, + "browser_*": false, + "atlassian_*": false, + "jira_get": true, + "confluence_*": false, + "todowrite": false, + "playwright": false, + "write": true, + "edit": true, + "bash": true + }, + "time": { "created": 1767726567752 }, + "id": "msg_b94b6d147001k2DbeF1m2JpIP3", + "agent": "nvim", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "title": "Debug alt-M keybind issue (@nvim subagent)", + "version": "1.0.220", + "time": { "created": 1767726567748, "updated": 1767726567766 }, + "directory": "/Users/matigreen/dotfiles", + "parentID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "summary": { "deletions": 0, "files": 0, "additions": 0 }, + "id": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "title": "Debugging alt-M keybind opencode.nvim", "diffs": [] }, + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "tools": { + "todoread": false, + "task": false, + "browser_*": false, + "atlassian_*": false, + "jira_get": true, + "confluence_*": false, + "todowrite": false, + "playwright": false, + "write": true, + "edit": true, + "bash": true + }, + "time": { "created": 1767726567752 }, + "id": "msg_b94b6d147001k2DbeF1m2JpIP3", + "agent": "nvim", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6de23001y1RTIdtsaHvsAw", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "delta": " the `", + "part": { + "type": "text", + "text": "I'll investigate the `", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6de240010A9XWmezoH5cz9", + "time": { "start": 1767726571044 }, + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "code.nvim.", + "part": { + "type": "text", + "text": "I'll investigate the `alt-M` keybind issue for opencode.nvim.", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6de240010A9XWmezoH5cz9", + "time": { "start": 1767726571044 }, + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " files", + "part": { + "type": "text", + "text": "I'll investigate the `alt-M` keybind issue for opencode.nvim. Let me start by examining the relevant configuration files", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6de240010A9XWmezoH5cz9", + "time": { "start": 1767726571044 }, + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "I'll investigate the `alt-M` keybind issue for opencode.nvim. Let me start by examining the relevant configuration files.", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6de240010A9XWmezoH5cz9", + "time": { "start": 1767726571695, "end": 1767726571695 }, + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01BbJ53KgPXPLXqVbMQhaPyT", + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [{ "tool": "read", "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", "state": { "status": "pending" } }], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726571700 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01BbJ53KgPXPLXqVbMQhaPyT", + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { + "status": "completed", + "output": "\n00001| return {\n00002| \"sudo-tee/opencode.nvim\",\n00003| config = function()\n00004| -- Check if custom configs are enabled\n00005| local custom_enabled = vim.g.opencode_custom_enabled == true\n00006| \n00007| if custom_enabled then\n00008| -- =========================================================================\n00009| -- CUSTOM CONFIGURATION MODE\n00010| -- =========================================================================\n00011| require(\"opencode\").setup({\n00012| default_mode = \"plan\", -- Default to Plan mode instead of Build\n00013| keymap = {\n00014| editor = {\n00015| [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n00016| [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n00017| [\"o\"] = { \"redo\", desc = \"Redo message\" },\n00018| -- Override default provider switcher with custom favorites\n00019| [\"op\"] = {\n00020| function()\n00021| require(\"gmati.plugins.ai.opencode_favorites\").select_favorite_provider()\n00022| end,\n00023| desc = \"Switch provider (favorites)\",\n00024| },\n00025| },\n00026| input_window = {\n00027| -- Enter submits only in normal mode\n00028| [\"\"] = { \"submit_input_prompt\", mode = \"n\" },\n00029| },\n00030| },\n00031| ui = {\n00032| -- Disable built-in status displays (we use lualine instead)\n00033| display_model = false,\n00034| display_context_size = false,\n00035| display_cost = false,\n00036| },\n00037| debug = {\n00038| capture_streamed_events = true\n00039| }\n00040| })\n00041| \n00042| -- Apply UI patches to customize OpenCode rendering\n00043| vim.defer_fn(function()\n00044| require(\"gmati.plugins.ai.opencode_ui_patches\").apply_all()\n00045| end, 0)\n00046| \n00047| -- Setup custom keymaps (mode switching, diagnostics, emergency)\n00048| require(\"gmati.plugins.ai.opencode_keymaps\").setup()\n00049| \n00050| -- Setup diagnostics monitoring (health checks, error tracking)\n00051| vim.defer_fn(function()\n00052| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00053| if ok then\n00054| diagnostics.setup()\n00055| end\n00056| end, 300)\n00057| \n00058| -- Setup LSP/features integration (disable during thinking)\n00059| vim.defer_fn(function()\n00060| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00061| if ok then\n00062| integration.setup()\n00063| end\n00064| end, 200)\n00065| \n00066| -- Cleanup on exit\n00067| vim.api.nvim_create_autocmd(\"VimLeavePre\", {\n00068| callback = function()\n00069| local ok, integration = pcall(require, \"gmati.plugins.ai.opencode_integration\")\n00070| if ok then\n00071| integration.cleanup()\n00072| end\n00073| \n00074| local ok_diag, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00075| if ok_diag then\n00076| diagnostics.cleanup()\n00077| end\n00078| end,\n00079| })\n00080| else\n00081| -- =========================================================================\n00082| -- VANILLA CONFIGURATION MODE\n00083| -- =========================================================================\n00084| require(\"opencode\").setup({\n00085| default_mode = \"plan\", -- Keep your default mode preference\n00086| debug = {\n00087| capture_streamed_events = true\n00088| }\n00089| })\n00090| end\n00091| end,\n00092| dependencies = {\n00093| \"nvim-lua/plenary.nvim\",\n00094| {\n00095| \"MeanderingProgrammer/render-markdown.nvim\",\n00096| opts = {\n00097| anti_conceal = { enabled = false },\n00098| file_types = { \"markdown\", \"opencode_output\" },\n00099| },\n00100| ft = { \"markdown\", \"Avante\", \"copilot-chat\", \"opencode_output\" },\n00101| },\n00102| -- Optional, for file mentions and commands completion\n00103| \"hrsh7th/nvim-cmp\",\n00104| -- Optional, for file mentions picker\n00105| \"folke/snacks.nvim\",\n00106| },\n00107| }\n00108| \n\n(End of file - total 108 lines)\n", + "metadata": { + "preview": "return {\n \"sudo-tee/opencode.nvim\",\n config = function()\n -- Check if custom configs are enabled\n local custom_enabled = vim.g.opencode_custom_enabled == true\n\n if custom_enabled then\n -- =========================================================================\n -- CUSTOM CONFIGURATION MODE\n -- =========================================================================\n require(\"opencode\").setup({\n default_mode = \"plan\", -- Default to Plan mode instead of Build\n keymap = {\n editor = {\n [\"o\"] = { \"switch_mode\", desc = \"Toggle agent (Plan/Build)\" },\n [\"ou\"] = { \"undo\", desc = \"Undo last message\" },\n [\"o\"] = { \"redo\", desc = \"Redo message\" },\n -- Override default provider switcher with custom favorites\n [\"op\"] = {\n function()" + }, + "input": { "filePath": "/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" }, + "time": { "start": 1767726572684, "end": 1767726572708 }, + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { "tool": "read", "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", "state": { "status": "pending" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726572710 }, + "status": "running" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01NLzNf8fQ5m6CbQmJi61LHi", + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01NLzNf8fQ5m6CbQmJi61LHi", + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { + "status": "completed", + "output": "\n00001| -- OpenCode Keymaps\n00002| -- All custom keybindings for OpenCode functionality\n00003| -- Organized by category for easy discovery and modification\n00004| \n00005| local M = {}\n00006| \n00007| -- Setup all OpenCode keymaps\n00008| function M.setup()\n00009| M.setup_mode_keymaps()\n00010| M.setup_diagnostic_keymaps()\n00011| M.setup_emergency_keymaps()\n00012| end\n00013| \n00014| -- Mode-related keymaps (switching modes, quick build)\n00015| function M.setup_mode_keymaps()\n00016| -- Quick build mode: Switch to build mode and prepare \"build\" command\n00017| vim.keymap.set(\"n\", \"o\", function()\n00018| require(\"gmati.plugins.ai.opencode_build_mode\").quick_build()\n00019| end, { desc = \"Build mode + prepare 'build'\" })\n00020| end\n00021| \n00022| -- Diagnostic keymaps (health status, logs)\n00023| function M.setup_diagnostic_keymaps()\n00024| -- Show OpenCode diagnostics (health status)\n00025| vim.keymap.set(\"n\", \"od\", function()\n00026| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00027| if ok then\n00028| diagnostics.show_health_status()\n00029| else\n00030| vim.notify(\"Failed to load OpenCode diagnostics\", vim.log.levels.ERROR)\n00031| end\n00032| end, { desc = \"Show OpenCode diagnostics\" })\n00033| \n00034| -- Open OpenCode server logs\n00035| vim.keymap.set(\"n\", \"oL\", function()\n00036| local ok, diagnostics = pcall(require, \"gmati.plugins.ai.opencode_diagnostics\")\n00037| if ok then\n00038| diagnostics.open_server_logs()\n00039| else\n00040| vim.notify(\"Failed to load OpenCode diagnostics\", vim.log.levels.ERROR)\n00041| end\n00042| end, { desc = \"View OpenCode server logs\" })\n00043| end\n00044| \n00045| -- Emergency keymaps (force stop)\n00046| function M.setup_emergency_keymaps()\n00047| -- Force stop OpenCode: Nuclear option to kill all jobs and reset state\n00048| vim.keymap.set(\"n\", \"oF\", function()\n00049| require(\"gmati.plugins.ai.opencode_force_stop\").force_stop()\n00050| end, { desc = \"Force stop OpenCode (nuclear option)\" })\n00051| end\n00052| \n00053| return M\n00054| \n\n(End of file - total 54 lines)\n", + "metadata": { + "preview": "-- OpenCode Keymaps\n-- All custom keybindings for OpenCode functionality\n-- Organized by category for easy discovery and modification\n\nlocal M = {}\n\n-- Setup all OpenCode keymaps\nfunction M.setup()\n M.setup_mode_keymaps()\n M.setup_diagnostic_keymaps()\n M.setup_emergency_keymaps()\nend\n\n-- Mode-related keymaps (switching modes, quick build)\nfunction M.setup_mode_keymaps()\n -- Quick build mode: Switch to build mode and prepare \"build\" command\n vim.keymap.set(\"n\", \"o\", function()\n require(\"gmati.plugins.ai.opencode_build_mode\").quick_build()\n end, { desc = \"Build mode + prepare 'build'\" })\nend" + }, + "input": { + "filePath": "/Users/matigreen/dotfiles/nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + }, + "time": { "start": 1767726573388, "end": 1767726573393 }, + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { "tool": "read", "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", "state": { "status": "pending" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726573417 }, + "status": "running" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01SkSniBkbNnBeJr1jwfEvvs", + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01SkSniBkbNnBeJr1jwfEvvs", + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "state": { + "status": "completed", + "output": "\n00001| initial-window = false\n00002| \n00003| quick-terminal-position = center\n00004| quick-terminal-size = 98%,95%\n00005| \n00006| window-padding-x = 16\n00007| window-padding-y = 16\n00008| window-padding-balance = true\n00009| \n00010| theme = catppuccin-mocha.conf\n00011| background-opacity = 0.9\n00012| background-blur-radius = 20\n00013| \n00014| font-family = \"Hack Nerd Font\"\n00015| font-size = 18\n00016| \n00017| # Enable Option key as Alt modifier for terminal keybindings\n00018| macos-option-as-alt = true\n00019| \n00020| keybind = global:cmd+f12=toggle_quick_terminal\n00021| \n00022| keybind = cmd+shift+k=goto_split:top\n00023| keybind = cmd+shift+j=goto_split:bottom\n00024| keybind = cmd+shift+h=goto_split:left\n00025| keybind = cmd+shift+l=goto_split:right\n00026| \n\n(End of file - total 26 lines)\n", + "metadata": { + "preview": "initial-window = false\n\nquick-terminal-position = center\nquick-terminal-size = 98%,95%\n\nwindow-padding-x = 16\nwindow-padding-y = 16\nwindow-padding-balance = true\n\ntheme = catppuccin-mocha.conf\nbackground-opacity = 0.9\nbackground-blur-radius = 20\n\nfont-family = \"Hack Nerd Font\"\nfont-size = 18\n\n# Enable Option key as Alt modifier for terminal keybindings\nmacos-option-as-alt = true\n\nkeybind = global:cmd+f12=toggle_quick_terminal" + }, + "input": { "filePath": "/Users/matigreen/dotfiles/ghostty/.config/ghostty/config" }, + "time": { "start": 1767726573681, "end": 1767726573695 }, + "title": "ghostty/.config/ghostty/config" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726573696 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-finish", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "reason": "tool-calls", + "messageID": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "id": "prt_b94b6e910001sHfwoYXshpCwWv", + "cost": 0, + "tokens": { "reasoning": 0, "input": 2, "output": 235, "cache": { "write": 24673, "read": 0 } } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "finish": "tool-calls", + "tokens": { "reasoning": 0, "input": 2, "output": 235, "cache": { "write": 24673, "read": 0 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726567755 }, + "id": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "finish": "tool-calls", + "tokens": { "reasoning": 0, "input": 2, "output": 235, "cache": { "write": 24673, "read": 0 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726567755, "completed": 1767726573914 }, + "id": "msg_b94b6d14b001y9ggcfX7Qmvfb4", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726573918 }, + "id": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "title": "Debugging alt-M keybind opencode.nvim", "diffs": [] }, + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "tools": { + "todoread": false, + "task": false, + "browser_*": false, + "atlassian_*": false, + "jira_get": true, + "confluence_*": false, + "todowrite": false, + "playwright": false, + "write": true, + "edit": true, + "bash": true + }, + "time": { "created": 1767726567752 }, + "id": "msg_b94b6d147001k2DbeF1m2JpIP3", + "agent": "nvim", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "title": "Debug alt-M keybind issue (@nvim subagent)", + "version": "1.0.220", + "time": { "created": 1767726567748, "updated": 1767726573935 }, + "directory": "/Users/matigreen/dotfiles", + "parentID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "summary": { "deletions": 0, "files": 0, "additions": 0 }, + "id": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f600001vrOB25LzH46DAo", + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "delta": " let", + "part": { + "type": "text", + "text": "Now let", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "code.", + "part": { + "type": "text", + "text": "Now let me check the opencode.", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " or", + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": " default", + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or source to understand what the default", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "alt", + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "` ke", + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` ke", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "delta": "d does", + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` keybind does", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726577153 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "Now let me check the opencode.nvim plugin documentation or source to understand what the default `alt-M` keybind does:", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b6f601001imJME5kdm4mlg6", + "time": { "start": 1767726578336, "end": 1767726578336 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_0135HE3hqPJhMmqYtvzz2xyu", + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + }, + { "tool": "bash", "id": "prt_b94b6faa0001LKlGQTIabbHKvg", "state": { "status": "pending" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726578336 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_0135HE3hqPJhMmqYtvzz2xyu", + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "state": { + "input": { + "command": "cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20", + "description": "Find opencode.nvim plugin files" + }, + "time": { "start": 1767726579322 }, + "status": "running" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + }, + { "tool": "bash", "id": "prt_b94b6faa0001LKlGQTIabbHKvg", "state": { "status": "running" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726579323 }, + "status": "running" + } + } + } + }, + { + "type": "permission.updated", + "properties": { + "title": "cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20", + "pattern": ["find . *", "head *"], + "type": "bash", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_0135HE3hqPJhMmqYtvzz2xyu", + "metadata": { + "command": "cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20", + "patterns": ["find . *", "head *"] + }, + "id": "per_b94b6fe95001R3s3aeqFb9wsLe", + "time": { "created": 1767726579349 }, + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "permission.replied", + "properties": { + "permissionID": "per_b94b6fe95001R3s3aeqFb9wsLe", + "response": "once", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_0135HE3hqPJhMmqYtvzz2xyu", + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "state": { + "status": "completed", + "output": "./tests/mocks/session_list.lua\n./tests/unit/hooks_spec.lua\n./tests/unit/timer_spec.lua\n./tests/unit/input_window_spec.lua\n./tests/unit/server_job_spec.lua\n./tests/unit/config_spec.lua\n./tests/unit/api_spec.lua\n./tests/unit/event_manager_spec.lua\n./tests/unit/init_spec.lua\n./tests/unit/state_spec.lua\n./tests/unit/image_handler_spec.lua\n./tests/unit/id_spec.lua\n./tests/unit/api_client_spec.lua\n./tests/unit/context_spec.lua\n./tests/unit/session_spec.lua\n./tests/unit/config_file_spec.lua\n./tests/unit/opencode_server_spec.lua\n./tests/unit/core_spec.lua\n./tests/unit/context_completion_spec.lua\n./tests/unit/render_state_spec.lua\n", + "metadata": { + "exit": 0, + "output": "./tests/mocks/session_list.lua\n./tests/unit/hooks_spec.lua\n./tests/unit/timer_spec.lua\n./tests/unit/input_window_spec.lua\n./tests/unit/server_job_spec.lua\n./tests/unit/config_spec.lua\n./tests/unit/api_spec.lua\n./tests/unit/event_manager_spec.lua\n./tests/unit/init_spec.lua\n./tests/unit/state_spec.lua\n./tests/unit/image_handler_spec.lua\n./tests/unit/id_spec.lua\n./tests/unit/api_client_spec.lua\n./tests/unit/context_spec.lua\n./tests/unit/session_spec.lua\n./tests/unit/config_file_spec.lua\n./tests/unit/opencode_server_spec.lua\n./tests/unit/core_spec.lua\n./tests/unit/context_completion_spec.lua\n./tests/unit/render_state_spec.lua\n", + "description": "Find opencode.nvim plugin files" + }, + "input": { + "command": "cd ~/.local/share/nvim/lazy/opencode.nvim && find . -name \"*.lua\" -type f | head -20", + "description": "Find opencode.nvim plugin files" + }, + "time": { "start": 1767726579322, "end": 1767726603966 }, + "title": "Find opencode.nvim plugin files" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + }, + { + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "state": { "status": "completed", "title": "Find opencode.nvim plugin files" } + } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726603967 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-finish", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "reason": "tool-calls", + "messageID": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "id": "prt_b94b75ec0001Fsf2UjpQApsRHh", + "cost": 0, + "tokens": { "reasoning": 0, "input": 6, "output": 136, "cache": { "write": 2712, "read": 24673 } } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "finish": "tool-calls", + "tokens": { "reasoning": 0, "input": 6, "output": 136, "cache": { "write": 2712, "read": 24673 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726573918 }, + "id": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "finish": "tool-calls", + "tokens": { "reasoning": 0, "input": 6, "output": 136, "cache": { "write": 2712, "read": 24673 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726573918, "completed": 1767726604035 }, + "id": "msg_b94b6e95e001xIoTqqCWgaqBfO", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "assistant", + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "parentID": "msg_b94b6d147001k2DbeF1m2JpIP3", + "providerID": "anthropic", + "time": { "created": 1767726604039 }, + "id": "msg_b94b75f07001FvPfzM3IrqRHw3", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "modelID": "claude-sonnet-4-5", + "mode": "nvim", + "cost": 0, + "agent": "nvim", + "path": { "cwd": "/Users/matigreen/dotfiles", "root": "/Users/matigreen/dotfiles" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "b8136e83cf077521b7177c9660d223a1759f881e", + "title": "Debug alt-M keybind issue (@nvim subagent)", + "version": "1.0.220", + "time": { "created": 1767726567748, "updated": 1767726604056 }, + "directory": "/Users/matigreen/dotfiles", + "parentID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "summary": { "deletions": 0, "files": 0, "additions": 0 }, + "id": "ses_46b492ebbffel5oIJ1qUl9GKFh" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "role": "user", + "summary": { "title": "Debugging alt-M keybind opencode.nvim", "diffs": [] }, + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "tools": { + "todoread": false, + "task": false, + "browser_*": false, + "atlassian_*": false, + "jira_get": true, + "confluence_*": false, + "todowrite": false, + "playwright": false, + "write": true, + "edit": true, + "bash": true + }, + "time": { "created": 1767726567752 }, + "id": "msg_b94b6d147001k2DbeF1m2JpIP3", + "agent": "nvim", + "model": { "providerID": "anthropic", "modelID": "claude-sonnet-4-5" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "29b76c5b84a59e892d23447cd82abb3afea86e41", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "id": "prt_b94b76a1d001uetcdCGKMa7pGM", + "messageID": "msg_b94b75f07001FvPfzM3IrqRHw3" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01XNzaXKr6tXjYSfRSikTw7Z", + "tool": "bash", + "id": "prt_b94b76a20001J4RYEL24y0dMrZ", + "messageID": "msg_b94b75f07001FvPfzM3IrqRHw3", + "state": { "input": {}, "raw": "", "status": "pending" } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + }, + { + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "state": { "status": "completed", "title": "Find opencode.nvim plugin files" } + }, + { "tool": "bash", "id": "prt_b94b76a20001J4RYEL24y0dMrZ", "state": { "status": "pending" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726606880 }, + "status": "running" + } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01XNzaXKr6tXjYSfRSikTw7Z", + "tool": "bash", + "id": "prt_b94b76a20001J4RYEL24y0dMrZ", + "messageID": "msg_b94b75f07001FvPfzM3IrqRHw3", + "state": { + "input": { + "command": "cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20", + "description": "Search for alt-M keybind in opencode.nvim" + }, + "time": { "start": 1767726607982 }, + "status": "running" + } + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "sessionID": "ses_46b499f08ffe3aldWmWZoH6N2m", + "callID": "toolu_017Li9bzxEHcp7pwBxRu4MxU", + "tool": "task", + "id": "prt_b94b6b5f3001JaaXlpjO9TFn2O", + "messageID": "msg_b94b6948d002pFurK7t7q4bSyb", + "state": { + "title": "Debug alt-M keybind issue", + "metadata": { + "summary": [ + { + "tool": "read", + "id": "prt_b94b6e0b3001mCVItg2EJfPAB2", + "state": { "status": "completed", "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua" } + }, + { + "tool": "read", + "id": "prt_b94b6e4a1001n0heEU3dEGxfWa", + "state": { + "status": "completed", + "title": "nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua" + } + }, + { + "tool": "read", + "id": "prt_b94b6e768001nwQ5wgnzVRZL8B", + "state": { "status": "completed", "title": "ghostty/.config/ghostty/config" } + }, + { + "tool": "bash", + "id": "prt_b94b6faa0001LKlGQTIabbHKvg", + "state": { "status": "completed", "title": "Find opencode.nvim plugin files" } + }, + { "tool": "bash", "id": "prt_b94b76a20001J4RYEL24y0dMrZ", "state": { "status": "running" } } + ], + "sessionId": "ses_46b492ebbffel5oIJ1qUl9GKFh" + }, + "input": { + "subagent_type": "nvim", + "prompt": "The user reports that the `alt-M` keybind does not work for the opencode.nvim plugin, even though it's the default keymap of the plugin.\n\nContext:\n- The opencode.nvim plugin is installed at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode.lua`\n- Custom configuration is enabled (`vim.g.opencode_custom_enabled == true`)\n- The setup includes custom keymaps that override some defaults\n- There's a custom keymaps module at `file://nvim/.config/nvim/lua/gmati/plugins/ai/opencode_keymaps.lua`\n\nPlease investigate:\n1. What is the default `alt-M` keybind supposed to do in opencode.nvim?\n2. Check if the custom configuration is overriding or disabling this keybind\n3. Review the `opencode_keymaps.lua` file to see if there are any conflicts\n4. Check if there are any macOS-specific issues with alt/option key bindings in the terminal (Ghostty)\n5. Provide a solution to restore the `alt-M` functionality\n\nReturn your findings and a recommended fix.", + "description": "Debug alt-M keybind issue" + }, + "time": { "start": 1767726607982 }, + "status": "running" + } + } + } + }, + { + "type": "permission.updated", + "properties": { + "title": "cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20", + "pattern": ["grep \"alt-M\\|\" *", "head *"], + "type": "bash", + "sessionID": "ses_46b492ebbffel5oIJ1qUl9GKFh", + "callID": "toolu_01XNzaXKr6tXjYSfRSikTw7Z", + "metadata": { + "command": "cd ~/.local/share/nvim/lazy/opencode.nvim && grep -r \"alt-M\\|\" --include=\"*.lua\" | head -20", + "patterns": ["grep \"alt-M\\|\" *", "head *"] + }, + "id": "per_b94b76e73001ml5seBvInCYYHG", + "time": { "created": 1767726607987 }, + "messageID": "msg_b94b75f07001FvPfzM3IrqRHw3" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/planning.expected.json b/tests/data/planning.expected.json index 00239ea6..b65135cc 100644 --- a/tests/data/planning.expected.json +++ b/tests/data/planning.expected.json @@ -1 +1,372 @@ -{"extmarks":[[1,1,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-11 17:41:45)","OpencodeHint"],[" [msg_9d45d40c9001s7A1sP3Ew537QN]","OpencodeHint"]],"virt_text_hide":false,"virt_text_win_col":-3}],[2,2,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"virt_text_win_col":-3}],[3,3,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"virt_text_win_col":-3}],[4,4,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"virt_text_win_col":-3}],[5,5,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_hide":false,"virt_text_win_col":-3}],[6,8,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-11 17:41:45)","OpencodeHint"],[" [msg_9d45d411b00254Lm5jVRwAeQxT]","OpencodeHint"]],"virt_text_hide":false,"virt_text_win_col":-3}],[7,12,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[8,13,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[9,14,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[10,15,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[11,16,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[12,19,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-11 17:41:51)","OpencodeHint"],[" [msg_9d45d585800269UgJnOLD8i2pF]","OpencodeHint"]],"virt_text_hide":false,"virt_text_win_col":-3}],[13,24,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-11 17:41:54)","OpencodeHint"],[" [msg_9d45d65b40026mDvwR5cCGTA30]","OpencodeHint"]],"virt_text_hide":false,"virt_text_win_col":-3}],[14,26,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[15,27,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[16,28,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[17,29,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[18,30,0,{"right_gravity":true,"ns_id":3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_hide":false,"virt_text_win_col":-1}],[19,33,0,{"right_gravity":true,"ns_id":3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-11 17:41:58)","OpencodeHint"],[" [msg_9d45d7390002yE2ve5szXtMdw0]","OpencodeHint"]],"virt_text_hide":false,"virt_text_win_col":-3}]],"timestamp":1769172200,"actions":[],"lines":["----","","","can you make a new neovim plugin for me?","","[`a-empty.txt`](a-empty.txt)","","----","","","I'll help you create a new Neovim plugin. Let me first examine your current setup and then create the plugin structure.","","**󰝖 plan** `4 todos`","- [ ] Examine existing Lua plugin structure ","- [ ] Create basic plugin directory structure ","- [ ] Write main plugin init.lua file ","- [ ] Create plugin documentation ","","----","","","** read** `/Users/cam/tmp/a/lua/example-plugin/init.lua`","","----","","","**󰝖 plan** `3 todos`","- [x] Examine existing Lua plugin structure ","- [-] Create basic plugin directory structure ","- [ ] Write main plugin init.lua file ","- [ ] Create plugin documentation ","","----","","","What would you like your Neovim plugin to do? I can see you have an example plugin structure already. I'll need to know:","","1. What functionality you want the plugin to provide","2. What you'd like to name it","3. Any specific features or commands you want to include","","Once you provide these details, I can create a complete plugin structure for you based on the pattern I see in your existing example-plugin.","",""]} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-11 17:41:45)", "OpencodeHint"], + [" [msg_9d45d40c9001s7A1sP3Ew537QN]", "OpencodeHint"] + ], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 2, + 2, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 3, + 3, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 4, + 4, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 5, + 5, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 6, + 8, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-11 17:41:45)", "OpencodeHint"], + [" [msg_9d45d411b00254Lm5jVRwAeQxT]", "OpencodeHint"] + ], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 7, + 12, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 8, + 13, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 9, + 14, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 10, + 15, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 11, + 16, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 12, + 19, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-11 17:41:51)", "OpencodeHint"], + [" [msg_9d45d585800269UgJnOLD8i2pF]", "OpencodeHint"] + ], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 13, + 24, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-11 17:41:54)", "OpencodeHint"], + [" [msg_9d45d65b40026mDvwR5cCGTA30]", "OpencodeHint"] + ], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ], + [ + 14, + 26, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 15, + 27, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 16, + 28, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 17, + 29, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 18, + 30, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_hide": false, + "virt_text_win_col": -1 + } + ], + [ + 19, + 33, + 0, + { + "right_gravity": true, + "ns_id": 3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-11 17:41:58)", "OpencodeHint"], + [" [msg_9d45d7390002yE2ve5szXtMdw0]", "OpencodeHint"] + ], + "virt_text_hide": false, + "virt_text_win_col": -3 + } + ] + ], + "timestamp": 1769172200, + "actions": [], + "lines": [ + "----", + "", + "", + "can you make a new neovim plugin for me?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "I'll help you create a new Neovim plugin. Let me first examine your current setup and then create the plugin structure.", + "", + "**󰝖 plan** `4 todos`", + "- [ ] Examine existing Lua plugin structure ", + "- [ ] Create basic plugin directory structure ", + "- [ ] Write main plugin init.lua file ", + "- [ ] Create plugin documentation ", + "", + "----", + "", + "", + "** read** `/Users/cam/tmp/a/lua/example-plugin/init.lua`", + "", + "----", + "", + "", + "**󰝖 plan** `3 todos`", + "- [x] Examine existing Lua plugin structure ", + "- [-] Create basic plugin directory structure ", + "- [ ] Write main plugin init.lua file ", + "- [ ] Create plugin documentation ", + "", + "----", + "", + "", + "What would you like your Neovim plugin to do? I can see you have an example plugin structure already. I'll need to know:", + "", + "1. What functionality you want the plugin to provide", + "2. What you'd like to name it", + "3. Any specific features or commands you want to include", + "", + "Once you provide these details, I can create a complete plugin structure for you based on the pattern I see in your existing example-plugin.", + "", + "" + ] +} diff --git a/tests/data/question-ask-replied.expected.json b/tests/data/question-ask-replied.expected.json index db08b5a5..a6469fc6 100644 --- a/tests/data/question-ask-replied.expected.json +++ b/tests/data/question-ask-replied.expected.json @@ -1 +1,230 @@ -{"extmarks":[[1,1,0,{"priority":10,"virt_text_pos":"win_col","virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-26 14:23:12)","OpencodeHint"],[" [msg_bfaafef41001700DjtCwvhFxse]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":false}],[2,2,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":true}],[3,3,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":true}],[4,4,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":true}],[5,5,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":true}],[6,6,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":true}],[7,8,0,{"priority":10,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:23:12)","OpencodeHint"],[" [msg_bfaafefb3001iBSmqfzFffDjCS]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":false}],[8,10,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"right_gravity":true,"virt_text_repeat_linebreak":true}],[9,11,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"right_gravity":true,"virt_text_repeat_linebreak":true}],[10,12,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"right_gravity":true,"virt_text_repeat_linebreak":true}],[11,13,0,{"priority":4096,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"right_gravity":true,"virt_text_repeat_linebreak":true}],[12,16,0,{"priority":10,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:23:22)","OpencodeHint"],[" [msg_bfab0156d001NyMxaTnj6Cml0c]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"right_gravity":true,"virt_text_repeat_linebreak":false}]],"timestamp":1769437626,"lines":["----","","","[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)","","can you use the question tool and ask me a question with an other choice","","----","","","** question** ","","**Q1:** What is your favorite color?","**A1:** Red","","----","","","Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!","",""],"actions":[]} +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "priority": 10, + "virt_text_pos": "win_col", + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-26 14:23:12)", "OpencodeHint"], + [" [msg_bfaafef41001700DjtCwvhFxse]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 4, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 5, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 6, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 7, + 8, + 0, + { + "priority": 10, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:23:12)", "OpencodeHint"], + [" [msg_bfaafefb3001iBSmqfzFffDjCS]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 8, + 10, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 11, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 12, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 11, + 13, + 0, + { + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 12, + 16, + 0, + { + "priority": 10, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:23:22)", "OpencodeHint"], + [" [msg_bfab0156d001NyMxaTnj6Cml0c]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ] + ], + "timestamp": 1769437626, + "lines": [ + "----", + "", + "", + "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", + "", + "can you use the question tool and ask me a question with an other choice", + "", + "----", + "", + "", + "** question** ", + "", + "**Q1:** What is your favorite color?", + "**A1:** Red", + "", + "----", + "", + "", + "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!", + "", + "" + ], + "actions": [] +} diff --git a/tests/data/question-ask-replied.json b/tests/data/question-ask-replied.json index b9276f67..a87bd319 100644 --- a/tests/data/question-ask-replied.json +++ b/tests/data/question-ask-replied.json @@ -1 +1,684 @@ -[{"type":"session.updated","properties":{"info":{"time":{"created":1769437382680,"updated":1769437382680},"title":"New session - 2026-01-26T14:23:02.680Z","version":"1.1.31","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"time":{"created":1769437392705},"role":"user","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"msg_bfaafef41001700DjtCwvhFxse","agent":"build"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","synthetic":true,"text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaafef42002jckWu6xA2ZV3zg","messageID":"msg_bfaafef41001700DjtCwvhFxse"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","synthetic":true,"text":"\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaafef47001HR3fZ3Ct7nScuT","messageID":"msg_bfaafef41001700DjtCwvhFxse"}}},{"type":"message.part.updated","properties":{"part":{"type":"file","messageID":"msg_bfaafef41001700DjtCwvhFxse","url":"file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","filename":"tests/replay/renderer_spec.lua","id":"prt_bfaafef47002OES5db7H5px77j","mime":"text/plain"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","text":"can you use the question tool and ask me a question with an other choice","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaafef42001XawiVVGpNU0t5E","messageID":"msg_bfaafef41001700DjtCwvhFxse"}}},{"type":"session.updated","properties":{"info":{"time":{"created":1769437382680,"updated":1769437392735},"title":"New session - 2026-01-26T14:23:02.680Z","version":"1.1.31","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","directory":"/home/francis/Projects/_nvim/opencode.nvim"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1769437392819},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","mode":"build","cost":0,"role":"assistant","tokens":{"input":0,"output":0,"reasoning":0,"cache":{"read":0,"write":0}},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","id":"msg_bfaafefb3001iBSmqfzFffDjCS","agent":"build"}}},{"type":"message.updated","properties":{"info":{"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"time":{"created":1769437392705},"role":"user","summary":{"diffs":[]},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"msg_bfaafef41001700DjtCwvhFxse","agent":"build"}}},{"type":"session.updated","properties":{"info":{"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769437382680,"updated":1769437392883},"title":"New session - 2026-01-26T14:23:02.680Z","version":"1.1.31","directory":"/home/francis/Projects/_nvim/opencode.nvim","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","summary":{"deletions":0,"additions":0,"files":0}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"617ad65111b912d010a8ea89957a9c5986bc54f0","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaaff532001RIiCvjxiMht1EK","messageID":"msg_bfaafefb3001iBSmqfzFffDjCS"}}},{"type":"message.part.updated","properties":{"part":{"type":"tool","callID":"call_b4hwTxyb1alO8r2uMlxeB873","tool":"question","state":{"status":"pending","raw":"","input":{}},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaaff539001jCeh626xIXgw9u","messageID":"msg_bfaafefb3001iBSmqfzFffDjCS"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.asked","properties":{"questions":[{"question":"What is your favorite color?","header":"Choose your preferred color","options":[{"description":"A bold, vibrant choice for those who like attention.","label":"Red"},{"description":"A calm, reliable color suitable for most environments.","label":"Blue"},{"description":"Symbolizes growth, harmony, and renewal.","label":"Green"},{"description":"Type your own favorite color.","label":"Other"}]}],"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","tool":{"messageID":"msg_bfaafefb3001iBSmqfzFffDjCS","callID":"call_b4hwTxyb1alO8r2uMlxeB873"},"id":"que_bfaaff9cc001DnGuMPilJYG9ya"}},{"type":"message.part.updated","properties":{"part":{"type":"tool","callID":"call_b4hwTxyb1alO8r2uMlxeB873","tool":"question","state":{"status":"running","time":{"start":1769437395404},"input":{"questions":[{"question":"What is your favorite color?","header":"Choose your preferred color","options":[{"description":"A bold, vibrant choice for those who like attention.","label":"Red"},{"description":"A calm, reliable color suitable for most environments.","label":"Blue"},{"description":"Symbolizes growth, harmony, and renewal.","label":"Green"},{"description":"Type your own favorite color.","label":"Other"}]}]}},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaaff539001jCeh626xIXgw9u","messageID":"msg_bfaafefb3001iBSmqfzFffDjCS"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"time":{"created":1769437392705},"role":"user","summary":{"title":"Question with Other choice","diffs":[]},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"msg_bfaafef41001700DjtCwvhFxse","agent":"build"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.replied","properties":{"answers":[["Red"]],"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","requestID":"que_bfaaff9cc001DnGuMPilJYG9ya"}},{"type":"message.part.updated","properties":{"part":{"type":"tool","callID":"call_b4hwTxyb1alO8r2uMlxeB873","tool":"question","state":{"metadata":{"answers":[["Red"]],"truncated":false},"time":{"start":1769437395404,"end":1769437402416},"title":"Asked 1 question","status":"completed","output":"User has answered your questions: \"What is your favorite color?\"=\"Red\". You can now continue with the user's answers in mind.","input":{"questions":[{"question":"What is your favorite color?","header":"Choose your preferred color","options":[{"description":"A bold, vibrant choice for those who like attention.","label":"Red"},{"description":"A calm, reliable color suitable for most environments.","label":"Blue"},{"description":"Symbolizes growth, harmony, and renewal.","label":"Green"},{"description":"Type your own favorite color.","label":"Other"}]}]}},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfaaff539001jCeh626xIXgw9u","messageID":"msg_bfaafefb3001iBSmqfzFffDjCS"}}},{"type":"message.part.updated","properties":{"part":{"type":"step-finish","tokens":{"input":12775,"output":150,"reasoning":0,"cache":{"read":0,"write":0}},"cost":0,"reason":"tool-calls","snapshot":"617ad65111b912d010a8ea89957a9c5986bc54f0","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01535001wSIZq9GBd5DM3p","messageID":"msg_bfaafefb3001iBSmqfzFffDjCS"}}},{"type":"message.updated","properties":{"info":{"time":{"created":1769437392819},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","tokens":{"input":12775,"output":150,"reasoning":0,"cache":{"read":0,"write":0}},"mode":"build","id":"msg_bfaafefb3001iBSmqfzFffDjCS","role":"assistant","cost":0,"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","finish":"tool-calls","agent":"build"}}},{"type":"message.updated","properties":{"info":{"time":{"created":1769437392819,"completed":1769437402460},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","tokens":{"input":12775,"output":150,"reasoning":0,"cache":{"read":0,"write":0}},"mode":"build","id":"msg_bfaafefb3001iBSmqfzFffDjCS","role":"assistant","cost":0,"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","finish":"tool-calls","agent":"build"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1769437402477},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","mode":"build","cost":0,"role":"assistant","tokens":{"input":0,"output":0,"reasoning":0,"cache":{"read":0,"write":0}},"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","id":"msg_bfab0156d001NyMxaTnj6Cml0c","agent":"build"}}},{"type":"message.updated","properties":{"info":{"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"time":{"created":1769437392705},"role":"user","summary":{"title":"Question with Other choice","diffs":[]},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"msg_bfaafef41001700DjtCwvhFxse","agent":"build"}}},{"type":"session.updated","properties":{"info":{"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769437382680,"updated":1769437402502},"title":"New session - 2026-01-26T14:23:02.680Z","version":"1.1.31","directory":"/home/francis/Projects/_nvim/opencode.nvim","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","summary":{"deletions":0,"additions":0,"files":0}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"step-start","snapshot":"617ad65111b912d010a8ea89957a9c5986bc54f0","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cd9001JINkAnbcXksgNO","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"}}},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":" you"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":" color"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question,","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":","}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":" example"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":" like"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":" proceed"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437404381},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"},"delta":"!"}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"type":"text","time":{"start":1769437405694,"end":1769437405694},"text":"Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab01cdd001Y6UnA89vh364ZV","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"}}},{"type":"message.part.updated","properties":{"part":{"type":"step-finish","tokens":{"input":874,"output":47,"reasoning":0,"cache":{"read":12032,"write":0}},"cost":0,"reason":"stop","snapshot":"617ad65111b912d010a8ea89957a9c5986bc54f0","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"prt_bfab021ff001EzP7TpA5rv7aTl","messageID":"msg_bfab0156d001NyMxaTnj6Cml0c"}}},{"type":"message.updated","properties":{"info":{"time":{"created":1769437402477},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","tokens":{"input":874,"output":47,"reasoning":0,"cache":{"read":12032,"write":0}},"mode":"build","id":"msg_bfab0156d001NyMxaTnj6Cml0c","role":"assistant","cost":0,"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","finish":"stop","agent":"build"}}},{"type":"message.updated","properties":{"info":{"time":{"created":1769437402477,"completed":1769437405729},"modelID":"gpt-4.1","providerID":"github-copilot","sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","tokens":{"input":874,"output":47,"reasoning":0,"cache":{"read":12032,"write":0}},"mode":"build","id":"msg_bfab0156d001NyMxaTnj6Cml0c","role":"assistant","cost":0,"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"parentID":"msg_bfaafef41001700DjtCwvhFxse","finish":"stop","agent":"build"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"model":{"providerID":"github-copilot","modelID":"gpt-4.1"},"time":{"created":1769437392705},"role":"user","summary":{"title":"Question with Other choice","diffs":[]},"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H","id":"msg_bfaafef41001700DjtCwvhFxse","agent":"build"}}},{"type":"session.idle","properties":{"sessionID":"ses_4055037e7ffe7iiIdzWGnWwd0H"}},{"type":"session.updated","properties":{"info":{"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769437382680,"updated":1769437405755},"title":"New session - 2026-01-26T14:23:02.680Z","version":"1.1.31","directory":"/home/francis/Projects/_nvim/opencode.nvim","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","summary":{"deletions":0,"additions":0,"files":0}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769437382680,"updated":1769437405755},"title":"Question with other choice for renderer_spec.lua","version":"1.1.31","directory":"/home/francis/Projects/_nvim/opencode.nvim","slug":"sunny-planet","id":"ses_4055037e7ffe7iiIdzWGnWwd0H","summary":{"deletions":0,"additions":0,"files":0}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]}] +[ + { + "type": "session.updated", + "properties": { + "info": { + "time": { "created": 1769437382680, "updated": 1769437382680 }, + "title": "New session - 2026-01-26T14:23:02.680Z", + "version": "1.1.31", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "time": { "created": 1769437392705 }, + "role": "user", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "msg_bfaafef41001700DjtCwvhFxse", + "agent": "build" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "synthetic": true, + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua\"}", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaafef42002jckWu6xA2ZV3zg", + "messageID": "msg_bfaafef41001700DjtCwvhFxse" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "synthetic": true, + "text": "\n00001| local state = require('opencode.state')\n00002| local ui = require('opencode.ui.ui')\n00003| local helpers = require('tests.helpers')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local assert = require('luassert')\n00006| local config = require('opencode.config')\n00007| \n00008| local function assert_output_matches(expected, actual, name)\n00009| local normalized_extmarks = helpers.normalize_namespace_ids(actual.extmarks)\n00010| \n00011| assert.are.equal(\n00012| #expected.lines,\n00013| #actual.lines,\n00014| string.format(\n00015| 'Line count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00016| #expected.lines,\n00017| #actual.lines,\n00018| math.min(#expected.lines, #actual.lines) + 1,\n00019| vim.inspect(expected.lines[math.min(#expected.lines, #actual.lines) + 1]),\n00020| vim.inspect(actual.lines[math.min(#expected.lines, #actual.lines) + 1])\n00021| )\n00022| )\n00023| \n00024| for i = 1, #expected.lines do\n00025| assert.are.equal(\n00026| expected.lines[i],\n00027| actual.lines[i],\n00028| string.format(\n00029| 'Line %d mismatch:\\n Expected: %s\\n Actual: %s',\n00030| i,\n00031| vim.inspect(expected.lines[i]),\n00032| vim.inspect(actual.lines[i])\n00033| )\n00034| )\n00035| end\n00036| \n00037| assert.are.equal(\n00038| #expected.extmarks,\n00039| #normalized_extmarks,\n00040| string.format(\n00041| 'Extmark count mismatch: expected %d, got %d.\\nFirst difference at index %d:\\n Expected: %s\\n Actual: %s',\n00042| #expected.extmarks,\n00043| #normalized_extmarks,\n00044| math.min(#expected.extmarks, #normalized_extmarks) + 1,\n00045| vim.inspect(expected.extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1]),\n00046| vim.inspect(normalized_extmarks[math.min(#expected.extmarks, #normalized_extmarks) + 1])\n00047| )\n00048| )\n00049| \n00050| for i = 1, #expected.extmarks do\n00051| assert.are.same(\n00052| expected.extmarks[i],\n00053| normalized_extmarks[i],\n00054| string.format(\n00055| 'Extmark %d mismatch:\\n Expected: %s\\n Actual: %s',\n00056| i,\n00057| vim.inspect(expected.extmarks[i]),\n00058| vim.inspect(normalized_extmarks[i])\n00059| )\n00060| )\n00061| end\n00062| \n00063| local expected_action_count = expected.actions and #expected.actions or 0\n00064| local actual_action_count = actual.actions and #actual.actions or 0\n00065| \n00066| assert.are.equal(\n00067| expected_action_count,\n00068| actual_action_count,\n00069| string.format('Action count mismatch: expected %d, got %d', expected_action_count, actual_action_count)\n00070| )\n00071| \n00072| if expected.actions then\n00073| -- Sort both arrays for consistent comparison since order doesn't matter\n00074| local function sort_actions(actions)\n00075| local sorted = vim.deepcopy(actions)\n00076| table.sort(sorted, function(a, b)\n00077| return vim.inspect(a) < vim.inspect(b)\n00078| end)\n00079| return sorted\n00080| end\n00081| \n00082| assert.same(\n00083| sort_actions(expected.actions),\n00084| sort_actions(actual.actions),\n00085| string.format(\n00086| 'Actions mismatch:\\n Expected: %s\\n Actual: %s',\n00087| vim.inspect(expected.actions),\n00088| vim.inspect(actual.actions)\n00089| )\n00090| )\n00091| end\n00092| end\n00093| \n00094| describe('renderer unit tests', function()\n00095| local event_subscriptions = {\n00096| 'session.updated',\n00097| 'session.compacted',\n00098| 'session.error',\n00099| 'message.updated',\n00100| 'message.removed',\n00101| 'message.part.updated',\n00102| 'message.part.removed',\n00103| 'permission.updated',\n00104| 'permission.replied',\n00105| 'question.replied',\n00106| 'question.asked',\n00107| 'file.edited',\n00108| 'custom.restore_point.created',\n00109| 'custom.emit_events.finished',\n00110| }\n00111| \n00112| before_each(function()\n00113| require('opencode.event_manager').setup()\n00114| end)\n00115| \n00116| it('subsribes to events correctly', function()\n00117| local renderer = require('opencode.ui.renderer')\n00118| local event_manager = state.event_manager\n00119| \n00120| event_manager.events = {}\n00121| \n00122| renderer.setup_subscriptions()\n00123| \n00124| for _, event_name in ipairs(event_subscriptions) do\n00125| assert.is_true(\n00126| event_manager.events[event_name] ~= nil,\n00127| string.format('Renderer did not subscribe to event: %s', event_name)\n00128| )\n00129| end\n00130| end)\n00131| \n00132| it('unsubsribes from events correctly', function()\n00133| local renderer = require('opencode.ui.renderer')\n00134| local event_manager = state.event_manager\n00135| \n00136| renderer.setup_subscriptions()\n00137| \n00138| renderer.setup_subscriptions(false)\n00139| \n00140| for _, event_name in ipairs(event_subscriptions) do\n00141| assert.is_true(\n00142| vim.tbl_isempty(event_manager.events[event_name]),\n00143| string.format('Renderer did not unsubscribe from event: %s', event_name)\n00144| )\n00145| end\n00146| end)\n00147| end)\n00148| \n00149| describe('renderer functional tests', function()\n00150| config.debug.show_ids = true\n00151| \n00152| before_each(function()\n00153| helpers.replay_setup()\n00154| end)\n00155| \n00156| after_each(function()\n00157| if state.windows then\n00158| ui.close_windows(state.windows)\n00159| end\n00160| end)\n00161| \n00162| local json_files = vim.fn.glob('tests/data/*.json', false, true)\n00163| \n00164| -- Don't do the full session test on these files, usually\n00165| -- because they involve permission prompts\n00166| local skip_full_session = {\n00167| 'permission-prompt',\n00168| 'permission-ask-new',\n00169| 'question-ask',\n00170| 'shifting-and-multiple-perms',\n00171| 'message-removal',\n00172| }\n00173| \n00174| for _, filepath in ipairs(json_files) do\n00175| local name = vim.fn.fnamemodify(filepath, ':t:r')\n00176| \n00177| if not name:match('%.expected$') then\n00178| local expected_path = 'tests/data/' .. name .. '.expected.json'\n00179| \n00180| if vim.fn.filereadable(expected_path) == 1 then\n00181| for i = 1, 2 do\n00182| config.ui.output.rendering.event_collapsing = i == 1 and true or false\n00183| it(\n00184| 'replays '\n00185| .. name\n00186| .. ' correctly (event-by-event, '\n00187| .. (config.ui.output.rendering.event_collapsing and 'collapsing' or 'no collapsing')\n00188| .. ')',\n00189| function()\n00190| local events = helpers.load_test_data(filepath)\n00191| state.active_session = helpers.get_session_from_events(events)\n00192| local expected = helpers.load_test_data(expected_path)\n00193| \n00194| helpers.replay_events(events)\n00195| vim.wait(1000, function()\n00196| return vim.tbl_isempty(state.event_manager.throttling_emitter.queue)\n00197| end)\n00198| \n00199| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00200| assert_output_matches(expected, actual, name)\n00201| end\n00202| )\n00203| end\n00204| \n00205| if not vim.tbl_contains(skip_full_session, name) then\n00206| it('replays ' .. name .. ' correctly (session)', function()\n00207| local renderer = require('opencode.ui.renderer')\n00208| local events = helpers.load_test_data(filepath)\n00209| state.active_session = helpers.get_session_from_events(events, true)\n00210| local expected = helpers.load_test_data(expected_path)\n00211| \n00212| local session_data = helpers.load_session_from_events(events)\n00213| renderer._render_full_session_data(session_data)\n00214| \n00215| local actual = helpers.capture_output(state.windows and state.windows.output_buf, output_window.namespace)\n00216| assert_output_matches(expected, actual, name)\n00217| end)\n00218| end\n00219| end\n00220| end\n00221| end\n00222| end)\n00223| \n\n(End of file - total 223 lines)\n", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaafef47001HR3fZ3Ct7nScuT", + "messageID": "msg_bfaafef41001700DjtCwvhFxse" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "file", + "messageID": "msg_bfaafef41001700DjtCwvhFxse", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/tests/replay/renderer_spec.lua", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "filename": "tests/replay/renderer_spec.lua", + "id": "prt_bfaafef47002OES5db7H5px77j", + "mime": "text/plain" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "text": "can you use the question tool and ask me a question with an other choice", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaafef42001XawiVVGpNU0t5E", + "messageID": "msg_bfaafef41001700DjtCwvhFxse" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "time": { "created": 1769437382680, "updated": 1769437392735 }, + "title": "New session - 2026-01-26T14:23:02.680Z", + "version": "1.1.31", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "directory": "/home/francis/Projects/_nvim/opencode.nvim" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437392819 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "mode": "build", + "cost": 0, + "role": "assistant", + "tokens": { "input": 0, "output": 0, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "id": "msg_bfaafefb3001iBSmqfzFffDjCS", + "agent": "build" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "time": { "created": 1769437392705 }, + "role": "user", + "summary": { "diffs": [] }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "msg_bfaafef41001700DjtCwvhFxse", + "agent": "build" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769437382680, "updated": 1769437392883 }, + "title": "New session - 2026-01-26T14:23:02.680Z", + "version": "1.1.31", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "617ad65111b912d010a8ea89957a9c5986bc54f0", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaaff532001RIiCvjxiMht1EK", + "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "callID": "call_b4hwTxyb1alO8r2uMlxeB873", + "tool": "question", + "state": { "status": "pending", "raw": "", "input": {} }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaaff539001jCeh626xIXgw9u", + "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.asked", + "properties": { + "questions": [ + { + "question": "What is your favorite color?", + "header": "Choose your preferred color", + "options": [ + { "description": "A bold, vibrant choice for those who like attention.", "label": "Red" }, + { "description": "A calm, reliable color suitable for most environments.", "label": "Blue" }, + { "description": "Symbolizes growth, harmony, and renewal.", "label": "Green" }, + { "description": "Type your own favorite color.", "label": "Other" } + ] + } + ], + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "tool": { "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS", "callID": "call_b4hwTxyb1alO8r2uMlxeB873" }, + "id": "que_bfaaff9cc001DnGuMPilJYG9ya" + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "callID": "call_b4hwTxyb1alO8r2uMlxeB873", + "tool": "question", + "state": { + "status": "running", + "time": { "start": 1769437395404 }, + "input": { + "questions": [ + { + "question": "What is your favorite color?", + "header": "Choose your preferred color", + "options": [ + { "description": "A bold, vibrant choice for those who like attention.", "label": "Red" }, + { "description": "A calm, reliable color suitable for most environments.", "label": "Blue" }, + { "description": "Symbolizes growth, harmony, and renewal.", "label": "Green" }, + { "description": "Type your own favorite color.", "label": "Other" } + ] + } + ] + } + }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaaff539001jCeh626xIXgw9u", + "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "time": { "created": 1769437392705 }, + "role": "user", + "summary": { "title": "Question with Other choice", "diffs": [] }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "msg_bfaafef41001700DjtCwvhFxse", + "agent": "build" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.replied", + "properties": { + "answers": [["Red"]], + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "requestID": "que_bfaaff9cc001DnGuMPilJYG9ya" + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "tool", + "callID": "call_b4hwTxyb1alO8r2uMlxeB873", + "tool": "question", + "state": { + "metadata": { "answers": [["Red"]], "truncated": false }, + "time": { "start": 1769437395404, "end": 1769437402416 }, + "title": "Asked 1 question", + "status": "completed", + "output": "User has answered your questions: \"What is your favorite color?\"=\"Red\". You can now continue with the user's answers in mind.", + "input": { + "questions": [ + { + "question": "What is your favorite color?", + "header": "Choose your preferred color", + "options": [ + { "description": "A bold, vibrant choice for those who like attention.", "label": "Red" }, + { "description": "A calm, reliable color suitable for most environments.", "label": "Blue" }, + { "description": "Symbolizes growth, harmony, and renewal.", "label": "Green" }, + { "description": "Type your own favorite color.", "label": "Other" } + ] + } + ] + } + }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfaaff539001jCeh626xIXgw9u", + "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-finish", + "tokens": { "input": 12775, "output": 150, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "cost": 0, + "reason": "tool-calls", + "snapshot": "617ad65111b912d010a8ea89957a9c5986bc54f0", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01535001wSIZq9GBd5DM3p", + "messageID": "msg_bfaafefb3001iBSmqfzFffDjCS" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437392819 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "tokens": { "input": 12775, "output": 150, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "mode": "build", + "id": "msg_bfaafefb3001iBSmqfzFffDjCS", + "role": "assistant", + "cost": 0, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "finish": "tool-calls", + "agent": "build" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437392819, "completed": 1769437402460 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "tokens": { "input": 12775, "output": 150, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "mode": "build", + "id": "msg_bfaafefb3001iBSmqfzFffDjCS", + "role": "assistant", + "cost": 0, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "finish": "tool-calls", + "agent": "build" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437402477 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "mode": "build", + "cost": 0, + "role": "assistant", + "tokens": { "input": 0, "output": 0, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "id": "msg_bfab0156d001NyMxaTnj6Cml0c", + "agent": "build" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "time": { "created": 1769437392705 }, + "role": "user", + "summary": { "title": "Question with Other choice", "diffs": [] }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "msg_bfaafef41001700DjtCwvhFxse", + "agent": "build" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769437382680, "updated": 1769437402502 }, + "title": "New session - 2026-01-26T14:23:02.680Z", + "version": "1.1.31", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-start", + "snapshot": "617ad65111b912d010a8ea89957a9c5986bc54f0", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cd9001JINkAnbcXksgNO", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": " you" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": " color" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question,", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": "," + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": " example" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": " like" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": " proceed" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437404381 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + }, + "delta": "!" + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "text", + "time": { "start": 1769437405694, "end": 1769437405694 }, + "text": "Thank you! You chose \"Red\" as your favorite color. If you have a specific task, question, or want to see this preference used in an example or feature, please let me know how you'd like to proceed!", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab01cdd001Y6UnA89vh364ZV", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "type": "step-finish", + "tokens": { "input": 874, "output": 47, "reasoning": 0, "cache": { "read": 12032, "write": 0 } }, + "cost": 0, + "reason": "stop", + "snapshot": "617ad65111b912d010a8ea89957a9c5986bc54f0", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "prt_bfab021ff001EzP7TpA5rv7aTl", + "messageID": "msg_bfab0156d001NyMxaTnj6Cml0c" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437402477 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "tokens": { "input": 874, "output": 47, "reasoning": 0, "cache": { "read": 12032, "write": 0 } }, + "mode": "build", + "id": "msg_bfab0156d001NyMxaTnj6Cml0c", + "role": "assistant", + "cost": 0, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "finish": "stop", + "agent": "build" + } + } + }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769437402477, "completed": 1769437405729 }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "tokens": { "input": 874, "output": 47, "reasoning": 0, "cache": { "read": 12032, "write": 0 } }, + "mode": "build", + "id": "msg_bfab0156d001NyMxaTnj6Cml0c", + "role": "assistant", + "cost": 0, + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "parentID": "msg_bfaafef41001700DjtCwvhFxse", + "finish": "stop", + "agent": "build" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "model": { "providerID": "github-copilot", "modelID": "gpt-4.1" }, + "time": { "created": 1769437392705 }, + "role": "user", + "summary": { "title": "Question with Other choice", "diffs": [] }, + "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "id": "msg_bfaafef41001700DjtCwvhFxse", + "agent": "build" + } + } + }, + { "type": "session.idle", "properties": { "sessionID": "ses_4055037e7ffe7iiIdzWGnWwd0H" } }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769437382680, "updated": 1769437405755 }, + "title": "New session - 2026-01-26T14:23:02.680Z", + "version": "1.1.31", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769437382680, "updated": 1769437405755 }, + "title": "Question with other choice for renderer_spec.lua", + "version": "1.1.31", + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "slug": "sunny-planet", + "id": "ses_4055037e7ffe7iiIdzWGnWwd0H", + "summary": { "deletions": 0, "additions": 0, "files": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/question-ask.expected.json b/tests/data/question-ask.expected.json index 04f4ae07..20c0e980 100644 --- a/tests/data/question-ask.expected.json +++ b/tests/data/question-ask.expected.json @@ -1 +1,326 @@ -{"actions":[],"extmarks":[[1,1,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2026-01-26 14:06:19)","OpencodeHint"],[" [msg_bfaa078a2001RUYhlKZhSlyWeF]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[2,2,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[3,3,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[4,4,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[5,5,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[6,6,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[7,8,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2026-01-26 14:06:19)","OpencodeHint"],[" [msg_bfaa078e7001qc33BqBlqzHv8X]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[8,13,0,{"right_gravity":true,"virt_text_win_col":-3,"virt_text":[[" ","OpencodeMessageRoleSystem"],[" "],["SYSTEM","OpencodeMessageRoleSystem"],["","OpencodeHint"],["","OpencodeHint"],[" [question-display-message]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[9,15,0,{"right_gravity":true,"priority":4096,"line_hl_group":"OpencodeQuestionTitle","ns_id":3}],[10,15,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[11,16,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[12,17,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[13,18,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[14,19,0,{"right_gravity":true,"priority":4096,"line_hl_group":"OpencodeDialogOptionHover","ns_id":3}],[15,19,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[16,19,2,{"virt_text":[["› ","OpencodeDialogOptionHover"]],"virt_text_hide":false,"ns_id":3,"right_gravity":true,"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":4096}],[17,20,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[18,21,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[19,22,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[20,23,0,{"right_gravity":true,"virt_text_win_col":-2,"virt_text":[["▌","OpencodeQuestionBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}]],"lines":["----","","","[`lua/opencode/ui/renderer.lua`](lua/opencode/ui/renderer.lua)","","can you use the question tool and ask me a question with an other choice","","----","","","** question** ","","----","",""," Question","","What is your favorite color?",""," 1. Red - The color red "," 2. Blue - The color blue"," 3. Other - Any color not listed","","Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``","",""],"timestamp":1769441972} +{ + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2026-01-26 14:06:19)", "OpencodeHint"], + [" [msg_bfaa078a2001RUYhlKZhSlyWeF]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 2, + 2, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 3, + 3, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 4, + 4, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 5, + 5, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 6, + 6, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 7, + 8, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2026-01-26 14:06:19)", "OpencodeHint"], + [" [msg_bfaa078e7001qc33BqBlqzHv8X]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 8, + 13, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text": [ + [" ", "OpencodeMessageRoleSystem"], + [" "], + ["SYSTEM", "OpencodeMessageRoleSystem"], + ["", "OpencodeHint"], + ["", "OpencodeHint"], + [" [question-display-message]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [9, 15, 0, { "right_gravity": true, "priority": 4096, "line_hl_group": "OpencodeQuestionTitle", "ns_id": 3 }], + [ + 10, + 15, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 11, + 16, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 12, + 17, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 13, + 18, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [14, 19, 0, { "right_gravity": true, "priority": 4096, "line_hl_group": "OpencodeDialogOptionHover", "ns_id": 3 }], + [ + 15, + 19, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 16, + 19, + 2, + { + "virt_text": [["› ", "OpencodeDialogOptionHover"]], + "virt_text_hide": false, + "ns_id": 3, + "right_gravity": true, + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 4096 + } + ], + [ + 17, + 20, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 18, + 21, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 19, + 22, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 20, + 23, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "virt_text": [["▌", "OpencodeQuestionBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ] + ], + "lines": [ + "----", + "", + "", + "[`lua/opencode/ui/renderer.lua`](lua/opencode/ui/renderer.lua)", + "", + "can you use the question tool and ask me a question with an other choice", + "", + "----", + "", + "", + "** question** ", + "", + "----", + "", + "", + " Question", + "", + "What is your favorite color?", + "", + " 1. Red - The color red ", + " 2. Blue - The color blue", + " 3. Other - Any color not listed", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``", + "", + "" + ], + "timestamp": 1769441972 +} diff --git a/tests/data/question-ask.json b/tests/data/question-ask.json index f3249bba..40e42689 100644 --- a/tests/data/question-ask.json +++ b/tests/data/question-ask.json @@ -1 +1,306 @@ -[{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","version":"1.1.31","slug":"calm-lagoon","id":"ses_4055f967bffeIHf3DnP0xntmxD","title":"New session - 2026-01-26T14:06:15.428Z","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769436375428,"updated":1769436375428}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1769436379298},"id":"msg_bfaa078a2001RUYhlKZhSlyWeF","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","role":"user","model":{"modelID":"gpt-4.1","providerID":"github-copilot"},"agent":"build"}}},{"type":"message.part.updated","properties":{"part":{"synthetic":true,"messageID":"msg_bfaa078a2001RUYhlKZhSlyWeF","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua\"}","type":"text","id":"prt_bfaa078a5001Pni4eaodfXL6o6"}}},{"type":"message.part.updated","properties":{"part":{"synthetic":true,"messageID":"msg_bfaa078a2001RUYhlKZhSlyWeF","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","text":"\n00001| local state = require('opencode.state')\n00002| local config = require('opencode.config')\n00003| local formatter = require('opencode.ui.formatter')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local permission_window = require('opencode.ui.permission_window')\n00006| local Promise = require('opencode.promise')\n00007| local RenderState = require('opencode.ui.render_state')\n00008| \n00009| local M = {\n00010| _prev_line_count = 0,\n00011| _render_state = RenderState.new(),\n00012| _last_part_formatted = {\n00013| part_id = nil,\n00014| formatted_data = nil --[[@as Output|nil]],\n00015| },\n00016| }\n00017| \n00018| local trigger_on_data_rendered = require('opencode.util').debounce(function()\n00019| local cb_type = type(config.ui.output.rendering.on_data_rendered)\n00020| \n00021| if cb_type == 'boolean' then\n00022| return\n00023| end\n00024| \n00025| if not state.windows or not state.windows.output_buf or not state.windows.output_win then\n00026| return\n00027| end\n00028| \n00029| if cb_type == 'function' then\n00030| pcall(config.ui.output.rendering.on_data_rendered, state.windows.output_buf, state.windows.output_win)\n00031| elseif vim.fn.exists(':RenderMarkdown') > 0 then\n00032| vim.cmd(':RenderMarkdown')\n00033| elseif vim.fn.exists(':Markview') > 0 then\n00034| vim.cmd(':Markview render ' .. state.windows.output_buf)\n00035| end\n00036| end, config.ui.output.rendering.markdown_debounce_ms or 250)\n00037| \n00038| ---Reset renderer state\n00039| function M.reset()\n00040| M._prev_line_count = 0\n00041| M._render_state:reset()\n00042| M._last_part_formatted = { part_id = nil, formatted_data = nil }\n00043| \n00044| output_window.clear()\n00045| \n00046| state.messages = {}\n00047| state.last_user_message = nil\n00048| state.tokens_count = 0\n00049| \n00050| local permissions = state.pending_permissions or {}\n00051| if #permissions and state.api_client then\n00052| for _, permission in ipairs(permissions) do\n00053| require('opencode.api').permission_deny(permission)\n00054| end\n00055| end\n00056| permission_window.clear_all()\n00057| state.pending_permissions = {}\n00058| \n00059| trigger_on_data_rendered()\n00060| end\n00061| \n00062| ---Set up event subscriptions\n00063| ---@param subscribe? boolean false to unsubscribe\n00064| function M.setup_subscriptions(subscribe)\n00065| subscribe = subscribe == nil and true or subscribe\n00066| \n00067| if subscribe then\n00068| state.subscribe('is_opencode_focused', M.on_focus_changed)\n00069| state.subscribe('active_session', M.on_session_changed)\n00070| else\n00071| state.unsubscribe('is_opencode_focused', M.on_focus_changed)\n00072| state.unsubscribe('active_session', M.on_session_changed)\n00073| end\n00074| \n00075| if not state.event_manager then\n00076| return\n00077| end\n00078| \n00079| local event_subscriptions = {\n00080| { 'session.updated', M.on_session_updated },\n00081| { 'session.compacted', M.on_session_compacted },\n00082| { 'session.error', M.on_session_error },\n00083| { 'message.updated', M.on_message_updated },\n00084| { 'message.removed', M.on_message_removed },\n00085| { 'message.part.updated', M.on_part_updated },\n00086| { 'message.part.removed', M.on_part_removed },\n00087| { 'permission.updated', M.on_permission_updated },\n00088| { 'permission.asked', M.on_permission_updated },\n00089| { 'permission.replied', M.on_permission_replied },\n00090| { 'question.asked', M.on_question_asked },\n00091| { 'question.replied', M.clear_question_display },\n00092| { 'question.rejected', M.clear_question_display },\n00093| { 'file.edited', M.on_file_edited },\n00094| { 'custom.restore_point.created', M.on_restore_points },\n00095| { 'custom.emit_events.finished', M.on_emit_events_finished },\n00096| }\n00097| \n00098| for _, sub in ipairs(event_subscriptions) do\n00099| if subscribe then\n00100| state.event_manager:subscribe(sub[1], sub[2])\n00101| else\n00102| state.event_manager:unsubscribe(sub[1], sub[2])\n00103| end\n00104| end\n00105| end\n00106| \n00107| ---Clean up and teardown renderer. Unsubscribes from all events\n00108| function M.teardown()\n00109| M.setup_subscriptions(false)\n00110| M.reset()\n00111| end\n00112| \n00113| ---Fetch full session messages from server\n00114| ---@return Promise Promise resolving to list of OpencodeMessage\n00115| local function fetch_session()\n00116| local session = state.active_session\n00117| if not session or not session or session == '' then\n00118| return Promise.new():resolve(nil)\n00119| end\n00120| \n00121| state.last_user_message = nil\n00122| return require('opencode.session').get_messages(session)\n00123| end\n00124| \n00125| ---Request all of the session data from the opencode server and render it\n00126| ---@return Promise\n00127| function M.render_full_session()\n00128| if not output_window.mounted() or not state.api_client then\n00129| return Promise.new():resolve(nil)\n00130| end\n00131| \n00132| return fetch_session():and_then(M._render_full_session_data)\n00133| end\n00134| \n00135| function M._render_full_session_data(session_data)\n00136| M.reset()\n00137| \n00138| if not state.active_session or not state.messages then\n00139| return\n00140| end\n00141| \n00142| local revert_index = nil\n00143| \n00144| -- if we're loading a session and there's no currently selected model, set it\n00145| -- from the messages\n00146| local set_mode_from_messages = not state.current_model\n00147| \n00148| for i, msg in ipairs(session_data) do\n00149| if state.active_session.revert and state.active_session.revert.messageID == msg.info.id then\n00150| revert_index = i\n00151| end\n00152| \n00153| M.on_message_updated({ info = msg.info }, revert_index)\n00154| \n00155| for _, part in ipairs(msg.parts or {}) do\n00156| M.on_part_updated({ part = part }, revert_index)\n00157| end\n00158| end\n00159| \n00160| if revert_index then\n00161| M._write_formatted_data(formatter._format_revert_message(state.messages, revert_index))\n00162| end\n00163| \n00164| if set_mode_from_messages then\n00165| M._set_model_and_mode_from_messages()\n00166| end\n00167| M.scroll_to_bottom()\n00168| \n00169| if config.hooks and config.hooks.on_session_loaded then\n00170| pcall(config.hooks.on_session_loaded, state.active_session)\n00171| end\n00172| end\n00173| \n00174| ---Append permissions display as a fake part at the end\n00175| function M.render_permissions_display()\n00176| local fake_message = {\n00177| info = {\n00178| id = 'permission-display-message',\n00179| sessionID = state.active_session and state.active_session.id or '',\n00180| role = 'system',\n00181| },\n00182| parts = {},\n00183| }\n00184| M.on_message_updated(fake_message)\n00185| \n00186| local fake_part = {\n00187| id = 'permission-display-part',\n00188| messageID = 'permission-display-message',\n00189| sessionID = state.active_session and state.active_session.id or '',\n00190| type = 'permissions-display',\n00191| }\n00192| \n00193| local permissions = permission_window.get_all_permissions()\n00194| if #permissions > 0 then\n00195| M.on_part_updated({ part = fake_part })\n00196| end\n00197| end\n00198| \n00199| function M.clear_question_display()\n00200| local question_window = require('opencode.ui.question_window')\n00201| question_window.clear_question()\n00202| M._remove_part_from_buffer('question-display-part')\n00203| M._remove_message_from_buffer('question-display-message')\n00204| end\n00205| \n00206| ---Render question display as a fake part\n00207| function M.render_question_display()\n00208| local question_window = require('opencode.ui.question_window')\n00209| \n00210| local current_question = question_window._current_question\n00211| \n00212| if not question_window.has_question() or not current_question or not current_question.id then\n00213| M._remove_part_from_buffer('question-display-part')\n00214| M._remove_message_from_buffer('question-display-message')\n00215| return\n00216| end\n00217| \n00218| local message_id = 'question-display-message'\n00219| local part_id = 'question-display-part'\n00220| \n00221| local fake_message = {\n00222| info = {\n00223| id = message_id,\n00224| sessionID = state.active_session and state.active_session.id or '',\n00225| role = 'system',\n00226| },\n00227| parts = {},\n00228| }\n00229| M.on_message_updated(fake_message)\n00230| \n00231| local fake_part = {\n00232| id = part_id,\n00233| messageID = message_id,\n00234| sessionID = state.active_session and state.active_session.id or '',\n00235| type = 'questions-display',\n00236| }\n00237| \n00238| M.on_part_updated({ part = fake_part })\n00239| M._redraw()\n00240| M.scroll_to_bottom(true)\n00241| end\n00242| \n00243| function M._redraw()\n00244| if state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win) then\n00245| vim.api.nvim_win_call(state.windows.output_win, function()\n00246| vim.cmd('redraw')\n00247| end)\n00248| end\n00249| end\n00250| \n00251| ---Render lines as the entire output buffer\n00252| ---@param lines any\n00253| function M.render_lines(lines)\n00254| local output = require('opencode.ui.output'):new()\n00255| output.lines = lines\n00256| M.render_output(output)\n00257| end\n00258| \n00259| ---Sets the entire output buffer based on output_data\n00260| ---@param output_data Output Output object from formatter\n00261| function M.render_output(output_data)\n00262| if not output_window.mounted() then\n00263| return\n00264| end\n00265| \n00266| local lines = output_data.lines or {}\n00267| \n00268| output_window.set_lines(lines)\n00269| output_window.clear_extmarks()\n00270| output_window.set_extmarks(output_data.extmarks)\n00271| M.scroll_to_bottom()\n00272| end\n00273| \n00274| ---Called when EventManager has finished emitting a batch of events\n00275| function M.on_emit_events_finished()\n00276| M.scroll_to_bottom()\n00277| end\n00278| \n00279| ---Find the most recently used model from the messages\n00280| function M._set_model_and_mode_from_messages()\n00281| if not state.messages then\n00282| return\n00283| end\n00284| \n00285| for i = #state.messages, 1, -1 do\n00286| local message = state.messages[i]\n00287| \n00288| if message and message.info then\n00289| if message.info.modelID and message.info.providerID then\n00290| state.current_model = message.info.providerID .. '/' .. message.info.modelID\n00291| if message.info.mode then\n00292| state.current_mode = message.info.mode\n00293| end\n00294| return\n00295| end\n00296| end\n00297| end\n00298| \n00299| -- we didn't find a model from any messages, set it to the default\n00300| require('opencode.core').initialize_current_model()\n00301| end\n00302| \n00303| ---Auto-scroll to bottom if user was already at bottom\n00304| ---Respects cursor position if user has scrolled up\n00305| ---@param force? boolean If true, scroll regardless of current position\n00306| function M.scroll_to_bottom(force)\n00307| if not state.windows or not state.windows.output_buf or not state.windows.output_win then\n00308| return\n00309| end\n00310| \n00311| if not vim.api.nvim_win_is_valid(state.windows.output_win) then\n00312| return\n00313| end\n00314| \n00315| local ok, line_count = pcall(vim.api.nvim_buf_line_count, state.windows.output_buf)\n00316| if not ok or line_count == 0 then\n00317| return\n00318| end\n00319| \n00320| local prev_line_count = M._prev_line_count or 0\n00321| \n00322| ---@cast line_count integer\n00323| M._prev_line_count = line_count\n00324| \n00325| trigger_on_data_rendered()\n00326| \n00327| -- Determine if we should scroll to bottom\n00328| local should_scroll = force == true\n00329| \n00330| if not should_scroll then\n00331| -- Always scroll on initial render\n00332| if prev_line_count == 0 then\n00333| should_scroll = true\n00334| -- Scroll if user is at bottom (respects manual scroll position)\n00335| elseif output_window.viewport_at_bottom then\n00336| should_scroll = true\n00337| end\n00338| end\n00339| \n00340| if should_scroll then\n00341| vim.api.nvim_win_set_cursor(state.windows.output_win, { line_count, 0 })\n00342| -- Use zb to position the cursor line at the bottom of the visible window\n00343| vim.api.nvim_win_call(state.windows.output_win, function()\n00344| vim.cmd('normal! zb')\n00345| end)\n00346| output_window.viewport_at_bottom = true\n00347| else\n00348| -- User has scrolled up, don't scroll\n00349| output_window.viewport_at_bottom = false\n00350| end\n00351| end\n00352| \n00353| ---Write data to output_buf, including normal text and extmarks\n00354| ---@param formatted_data Output Formatted data as Output object\n00355| ---@param part_id? string Optional part ID to store actions\n00356| ---@param start_line? integer Optional line to insert at (shifts content down). If nil, appends to end of buffer.\n00357| ---@return {line_start: integer, line_end: integer}? Range where data was written\n00358| function M._write_formatted_data(formatted_data, part_id, start_line)\n00359| if not state.windows or not state.windows.output_buf then\n00360| return\n00361| end\n00362| \n00363| local buf = state.windows.output_buf\n00364| local is_insertion = start_line ~= nil\n00365| local target_line = start_line or output_window.get_buf_line_count()\n00366| local new_lines = formatted_data.lines\n00367| local extmarks = formatted_data.extmarks\n00368| \n00369| if #new_lines == 0 or not buf then\n00370| return nil\n00371| end\n00372| \n00373| if is_insertion then\n00374| output_window.set_lines(new_lines, target_line, target_line)\n00375| else\n00376| local extra_newline = vim.tbl_extend('keep', {}, new_lines)\n00377| table.insert(extra_newline, '')\n00378| target_line = target_line - 1\n00379| output_window.set_lines(extra_newline, target_line)\n00380| end\n00381| \n00382| -- update actions and extmarks after the insertion because that may\n00383| -- adjust target_line (e.g. when we we're replacing the double newline at\n00384| -- the end)\n00385| \n00386| if part_id and formatted_data.actions then\n00387| M._render_state:add_actions(part_id, formatted_data.actions, target_line)\n00388| end\n00389| \n00390| output_window.set_extmarks(extmarks, target_line)\n00391| \n00392| return {\n00393| line_start = target_line,\n00394| line_end = target_line + #new_lines - 1,\n00395| }\n00396| end\n00397| \n00398| ---Insert new part, either at end of buffer or in the middle for out-of-order parts\n00399| ---@param part_id string Part ID\n00400| ---@param formatted_data Output Formatted data as Output object\n00401| ---@return boolean Success status\n00402| function M._insert_part_to_buffer(part_id, formatted_data)\n00403| local cached = M._render_state:get_part(part_id)\n00404| if not cached then\n00405| return false\n00406| end\n00407| \n00408| if #formatted_data.lines == 0 then\n00409| return true\n00410| end\n00411| \n00412| local is_current_message = state.current_message\n00413| and state.current_message.info\n00414| and state.current_message.info.id == cached.message_id\n00415| \n00416| if is_current_message then\n00417| -- NOTE: we're inserting a part for the current message, just add it to the end\n00418| \n00419| local range = M._write_formatted_data(formatted_data, part_id)\n00420| if not range then\n00421| return false\n00422| end\n00423| \n00424| M._render_state:set_part(cached.part, range.line_start, range.line_end)\n00425| \n00426| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00427| \n00428| return true\n00429| end\n00430| \n00431| -- NOTE: We're inserting a part for the first time for a previous message. We need to find\n00432| -- the insertion line (after the last part of this message or after the message header if\n00433| -- no parts).\n00434| local insertion_line = M._get_insertion_point_for_part(part_id, cached.message_id)\n00435| if not insertion_line then\n00436| return false\n00437| end\n00438| \n00439| local range = M._write_formatted_data(formatted_data, part_id, insertion_line)\n00440| if not range then\n00441| return false\n00442| end\n00443| \n00444| local line_count = #formatted_data.lines\n00445| M._render_state:shift_all(insertion_line, line_count)\n00446| \n00447| M._render_state:set_part(cached.part, range.line_start, range.line_end)\n00448| \n00449| return true\n00450| end\n00451| \n00452| ---Replace existing part in buffer\n00453| ---Adjusts line positions of subsequent parts if line count changes\n00454| ---@param part_id string Part ID\n00455| ---@param formatted_data Output Formatted data as Output object\n00456| ---@return boolean Success status\n00457| function M._replace_part_in_buffer(part_id, formatted_data)\n00458| local cached = M._render_state:get_part(part_id)\n00459| if not cached or not cached.line_start or not cached.line_end then\n00460| return false\n00461| end\n00462| \n00463| local new_lines = formatted_data.lines\n00464| local new_line_count = #new_lines\n00465| \n00466| local old_formatted = M._last_part_formatted\n00467| local can_optimize = old_formatted\n00468| and old_formatted.part_id == part_id\n00469| and old_formatted.formatted_data\n00470| and old_formatted.formatted_data.lines\n00471| \n00472| local lines_to_write = new_lines\n00473| local write_start_line = cached.line_start\n00474| \n00475| if can_optimize then\n00476| -- NOTE: This is an optimization to only replace the lines that are different\n00477| -- if we're replacing the most recently formatted part.\n00478| \n00479| ---@cast old_formatted { formatted_data: { lines: string[] } }\n00480| local old_lines = old_formatted.formatted_data.lines\n00481| local first_diff_line = nil\n00482| \n00483| -- Find the first line that's different\n00484| for i = 1, math.min(#old_lines, new_line_count) do\n00485| if old_lines[i] ~= new_lines[i] then\n00486| first_diff_line = i\n00487| break\n00488| end\n00489| end\n00490| \n00491| if not first_diff_line and new_line_count > #old_lines then\n00492| -- The old lines all matched but maybe there are more new lines\n00493| first_diff_line = #old_lines + 1\n00494| end\n00495| \n00496| if first_diff_line then\n00497| lines_to_write = vim.list_slice(new_lines, first_diff_line, new_line_count)\n00498| write_start_line = cached.line_start + first_diff_line - 1\n00499| elseif new_line_count == #old_lines then\n00500| -- Nothing was different, so we're done\n00501| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00502| return true\n00503| end\n00504| end\n00505| \n00506| M._render_state:clear_actions(part_id)\n00507| \n00508| output_window.clear_extmarks(cached.line_start - 1, cached.line_end + 1)\n00509| output_window.set_lines(lines_to_write, write_start_line, cached.line_end + 1)\n00510| \n00511| local new_line_end = cached.line_start + new_line_count - 1\n00512| \n00513| output_window.set_extmarks(formatted_data.extmarks, cached.line_start)\n00514| \n00515| if formatted_data.actions then\n00516| M._render_state:add_actions(part_id, formatted_data.actions, cached.line_start + 1)\n00517| end\n00518| \n00519| M._render_state:update_part_lines(part_id, cached.line_start, new_line_end)\n00520| \n00521| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00522| \n00523| return true\n00524| end\n00525| \n00526| ---Remove part from buffer and adjust subsequent line positions\n00527| ---@param part_id string Part ID\n00528| function M._remove_part_from_buffer(part_id)\n00529| local cached = M._render_state:get_part(part_id)\n00530| if not cached or not cached.line_start or not cached.line_end then\n00531| return\n00532| end\n00533| \n00534| if not state.windows or not state.windows.output_buf then\n00535| return\n00536| end\n00537| \n00538| output_window.clear_extmarks(cached.line_start - 1, cached.line_end)\n00539| output_window.set_lines({}, cached.line_start - 1, cached.line_end)\n00540| \n00541| M._render_state:remove_part(part_id)\n00542| end\n00543| \n00544| ---Remove message header from buffer and adjust subsequent line positions\n00545| ---@param message_id string Message ID\n00546| function M._remove_message_from_buffer(message_id)\n00547| local cached = M._render_state:get_message(message_id)\n00548| if not cached or not cached.line_start or not cached.line_end then\n00549| return\n00550| end\n00551| \n00552| if not state.windows or not state.windows.output_buf then\n00553| return\n00554| end\n00555| \n00556| output_window.clear_extmarks(cached.line_start - 1, cached.line_end)\n00557| output_window.set_lines({}, cached.line_start - 1, cached.line_end)\n00558| \n00559| M._render_state:remove_message(message_id)\n00560| end\n00561| \n00562| ---Adds a message (most likely just a header) to the buffer\n00563| ---@param message OpencodeMessage Message to add\n00564| function M._add_message_to_buffer(message)\n00565| local header_data = formatter.format_message_header(message)\n00566| local range = M._write_formatted_data(header_data)\n00567| \n00568| if range then\n00569| M._render_state:set_message(message, range.line_start, range.line_end)\n00570| end\n00571| end\n00572| \n00573| ---Replace existing message header in buffer\n00574| ---@param message_id string Message ID\n00575| ---@param formatted_data Output Formatted header as Output object\n00576| ---@return boolean Success status\n00577| function M._replace_message_in_buffer(message_id, formatted_data)\n00578| local cached = M._render_state:get_message(message_id)\n00579| if not cached or not cached.line_start or not cached.line_end then\n00580| return false\n00581| end\n00582| \n00583| local new_lines = formatted_data.lines\n00584| local new_line_count = #new_lines\n00585| \n00586| output_window.clear_extmarks(cached.line_start, cached.line_end + 1)\n00587| output_window.set_lines(new_lines, cached.line_start, cached.line_end + 1)\n00588| output_window.set_extmarks(formatted_data.extmarks, cached.line_start)\n00589| \n00590| local old_line_end = cached.line_end\n00591| local new_line_end = cached.line_start + new_line_count - 1\n00592| \n00593| M._render_state:set_message(cached.message, cached.line_start, new_line_end)\n00594| \n00595| local delta = new_line_end - old_line_end\n00596| if delta ~= 0 then\n00597| M._render_state:shift_all(old_line_end + 1, delta)\n00598| end\n00599| \n00600| return true\n00601| end\n00602| \n00603| ---Event handler for message.updated events\n00604| ---Creates new message or updates existing message info\n00605| ---@param message {info: MessageInfo} Event properties\n00606| ---@param revert_index? integer Revert index in session, if applicable\n00607| function M.on_message_updated(message, revert_index)\n00608| if not state.active_session or not state.messages then\n00609| return\n00610| end\n00611| \n00612| local msg = message --[[@as OpencodeMessage]]\n00613| if not msg or not msg.info or not msg.info.id or not msg.info.sessionID then\n00614| return\n00615| end\n00616| \n00617| if state.active_session.id ~= msg.info.sessionID then\n00618| ---@TODO This is probably a child session message, handle differently?\n00619| -- vim.notify('Session id does not match, discarding message: ' .. vim.inspect(message), vim.log.levels.WARN)\n00620| return\n00621| end\n00622| \n00623| local rendered_message = M._render_state:get_message(msg.info.id)\n00624| local found_msg = rendered_message and rendered_message.message\n00625| \n00626| if revert_index then\n00627| if not found_msg then\n00628| table.insert(state.messages, msg)\n00629| end\n00630| M._render_state:set_message(msg, 0, 0)\n00631| return\n00632| end\n00633| \n00634| if found_msg then\n00635| local error_changed = not vim.deep_equal(found_msg.info.error, msg.info.error)\n00636| \n00637| found_msg.info = msg.info\n00638| \n00639| --- NOTE: error handling is a bit messy because errors come in on messages\n00640| --- but we want to display the error at the end. In this case, we an error\n00641| --- was added to this message. We find the last part and re-render it to\n00642| --- display the message. If there are no parts, we'll re-render the message\n00643| \n00644| if error_changed and not revert_index then\n00645| local last_part_id = M._get_last_part_for_message(found_msg)\n00646| if last_part_id then\n00647| M._rerender_part(last_part_id)\n00648| else\n00649| local header_data = formatter.format_message_header(found_msg)\n00650| M._replace_message_in_buffer(msg.info.id, header_data)\n00651| end\n00652| end\n00653| else\n00654| table.insert(state.messages, msg)\n00655| \n00656| M._add_message_to_buffer(msg)\n00657| \n00658| state.current_message = msg\n00659| if message.info.role == 'user' then\n00660| state.last_user_message = msg\n00661| end\n00662| end\n00663| \n00664| M._update_stats_from_message(msg)\n00665| end\n00666| \n00667| ---Event handler for message.part.updated events\n00668| ---Inserts new parts or replaces existing parts in buffer\n00669| ---@param properties {part: OpencodeMessagePart} Event properties\n00670| ---@param revert_index? integer Revert index in session, if applicable\n00671| function M.on_part_updated(properties, revert_index)\n00672| if not properties or not properties.part or not state.active_session then\n00673| return\n00674| end\n00675| \n00676| local part = properties.part\n00677| if not part.id or not part.messageID or not part.sessionID then\n00678| return\n00679| end\n00680| \n00681| if state.active_session.id ~= part.sessionID then\n00682| ---@TODO This is probably a child session part, handle differently?\n00683| -- vim.notify('Session id does not match, discarding part: ' .. vim.inspect(part), vim.log.levels.WARN)\n00684| return\n00685| end\n00686| \n00687| local rendered_message = M._render_state:get_message(part.messageID)\n00688| if not rendered_message or not rendered_message.message then\n00689| vim.notify('Could not find message for part: ' .. vim.inspect(part), vim.log.levels.WARN)\n00690| return\n00691| end\n00692| \n00693| local message = rendered_message.message\n00694| \n00695| message.parts = message.parts or {}\n00696| \n00697| local part_data = M._render_state:get_part(part.id)\n00698| local is_new_part = not part_data\n00699| \n00700| local prev_last_part_id = M._get_last_part_for_message(message)\n00701| local is_last_part = is_new_part or (prev_last_part_id == part.id)\n00702| \n00703| if is_new_part then\n00704| table.insert(message.parts, part)\n00705| else\n00706| for i = #message.parts, 1, -1 do\n00707| if message.parts[i].id == part.id then\n00708| message.parts[i] = part\n00709| break\n00710| end\n00711| end\n00712| end\n00713| \n00714| if part.type == 'step-start' or part.type == 'step-finish' then\n00715| return\n00716| end\n00717| \n00718| if is_new_part then\n00719| M._render_state:set_part(part)\n00720| else\n00721| local rendered_part = M._render_state:update_part_data(part)\n00722| -- NOTE: This isn't the first time we've seen the part but we haven't rendered it previously\n00723| -- so try and render it this time by setting is_new_part = true (otherwise we'd call\n00724| -- _replace_message_in_buffer and it wouldn't do anything because the part hasn't been rendered)\n00725| if not rendered_part or (not rendered_part.line_start and not rendered_part.line_end) then\n00726| is_new_part = true\n00727| end\n00728| end\n00729| \n00730| local formatted = formatter.format_part(part, message, is_last_part)\n00731| \n00732| if revert_index and is_new_part then\n00733| return\n00734| end\n00735| \n00736| if is_new_part then\n00737| M._insert_part_to_buffer(part.id, formatted)\n00738| \n00739| if message.info.error then\n00740| --- NOTE: More error display code. As mentioned above, errors come in on messages\n00741| --- but we want to display them after parts so we tack the error onto the last\n00742| --- part. When a part is added and there's an error, we need to rerender\n00743| --- previous last part so it doesn't also display the message. If there was no previous\n00744| --- part, then we need to rerender the header so it doesn't display the error\n00745| \n00746| if not prev_last_part_id then\n00747| -- no previous part, we're the first part, re-render the message header\n00748| -- so it doesn't also display the error\n00749| local header_data = formatter.format_message_header(message)\n00750| M._replace_message_in_buffer(part.messageID, header_data)\n00751| elseif prev_last_part_id ~= part.id then\n00752| M._rerender_part(prev_last_part_id)\n00753| end\n00754| end\n00755| else\n00756| M._replace_part_in_buffer(part.id, formatted)\n00757| end\n00758| \n00759| if (part.type == 'file' or part.type == 'agent') and part.source then\n00760| -- we have a mention, we need to rerender the early part to highlight\n00761| -- the mention.\n00762| local text_part_id = M._find_text_part_for_message(message)\n00763| if text_part_id then\n00764| M._rerender_part(text_part_id)\n00765| end\n00766| end\n00767| end\n00768| \n00769| ---Event handler for message.part.removed events\n00770| ---@param properties {sessionID: string, messageID: string, partID: string} Event properties\n00771| function M.on_part_removed(properties)\n00772| if not properties then\n00773| return\n00774| end\n00775| \n00776| local part_id = properties.partID\n00777| if not part_id then\n00778| return\n00779| end\n00780| \n00781| local cached = M._render_state:get_part(part_id)\n00782| if cached and cached.message_id then\n00783| local rendered_message = M._render_state:get_message(cached.message_id)\n00784| if rendered_message and rendered_message.message then\n00785| local message = rendered_message.message\n00786| if message.parts then\n00787| for i, part in ipairs(message.parts) do\n00788| if part.id == part_id then\n00789| table.remove(message.parts, i)\n00790| break\n00791| end\n00792| end\n00793| end\n00794| end\n00795| end\n00796| \n00797| M._remove_part_from_buffer(part_id)\n00798| end\n00799| \n00800| ---Event handler for message.removed events\n00801| ---Removes message and all its parts from buffer\n00802| ---@param properties {sessionID: string, messageID: string} Event properties\n00803| function M.on_message_removed(properties)\n00804| if not properties or not state.messages then\n00805| return\n00806| end\n00807| \n00808| local message_id = properties.messageID\n00809| if not message_id then\n00810| return\n00811| end\n00812| \n00813| local rendered_message = M._render_state:get_message(message_id)\n00814| if not rendered_message or not rendered_message.message then\n00815| return\n00816| end\n00817| \n00818| local message = rendered_message.message\n00819| for _, part in ipairs(message.parts or {}) do\n00820| if part.id then\n00821| M._remove_part_from_buffer(part.id)\n00822| end\n00823| end\n00824| \n00825| M._remove_message_from_buffer(message_id)\n00826| \n00827| for i, msg in ipairs(state.messages or {}) do\n00828| if msg.info.id == message_id then\n00829| table.remove(state.messages, i)\n00830| break\n00831| end\n00832| end\n00833| end\n00834| \n00835| ---Event handler for session.compacted events\n00836| ---@param properties {sessionID: string} Event properties\n00837| function M.on_session_compacted(properties)\n00838| vim.notify('Session has been compacted')\n00839| end\n00840| \n00841| ---Event handler for session.updated events\n00842| ---@param properties {info: Session}\n00843| function M.on_session_updated(properties)\n00844| if not properties or not properties.info or not state.active_session then\n00845| return\n00846| end\n00847| if not vim.deep_equal(state.active_session.revert, properties.info.revert) then\n00848| state.active_session.revert = properties.info.revert\n00849| M._render_full_session_data(state.messages)\n00850| end\n00851| end\n00852| \n00853| ---Event handler for session.error events\n00854| ---@param properties {sessionID: string, error: table} Event properties\n00855| function M.on_session_error(properties)\n00856| if not properties or not properties.error then\n00857| return\n00858| end\n00859| \n00860| -- NOTE: we're handling message errors so session errors seem duplicative\n00861| if config.debug.enabled then\n00862| vim.notify('Session error: ' .. vim.inspect(properties.error))\n00863| end\n00864| end\n00865| \n00866| ---Event handler for permission.updated events\n00867| ---Re-renders part that requires permission and adds to permission window\n00868| ---@param permission OpencodePermission Event properties\n00869| function M.on_permission_updated(permission)\n00870| local tool = permission.tool\n00871| \n00872| ---@TODO this is for backward compatibility, remove later\n00873| local callID = tool and tool.callID or permission.callID\n00874| local messageID = tool and tool.messageID or permission.messageID\n00875| \n00876| if not permission or not messageID or not callID then\n00877| return\n00878| end\n00879| \n00880| -- Add permission to pending queue\n00881| if not state.pending_permissions then\n00882| state.pending_permissions = {}\n00883| end\n00884| \n00885| -- Check if permission already exists in queue\n00886| local existing_index = nil\n00887| for i, existing in ipairs(state.pending_permissions) do\n00888| if existing.id == permission.id then\n00889| existing_index = i\n00890| break\n00891| end\n00892| end\n00893| \n00894| local permissions = vim.deepcopy(state.pending_permissions)\n00895| if existing_index then\n00896| permissions[existing_index] = permission\n00897| else\n00898| table.insert(permissions, permission)\n00899| end\n00900| state.pending_permissions = permissions\n00901| \n00902| permission_window.add_permission(permission)\n00903| \n00904| M.render_permissions_display()\n00905| \n00906| M._rerender_part('permission-display-part')\n00907| M.scroll_to_bottom()\n00908| end\n00909| \n00910| ---Event handler for permission.replied events\n00911| ---Re-renders part after permission is resolved and removes from window\n00912| ---@param properties {sessionID: string, permissionID?: string,requestID?: string, response: string}|{} Event properties\n00913| function M.on_permission_replied(properties)\n00914| if not properties then\n00915| return\n00916| end\n00917| \n00918| local permission_id = properties.permissionID or properties.requestID\n00919| \n00920| if permission_id then\n00921| permission_window.remove_permission(permission_id)\n00922| state.pending_permissions = vim.deepcopy(permission_window.get_all_permissions())\n00923| if #state.pending_permissions == 0 then\n00924| M._remove_part_from_buffer('permission-display-part')\n00925| M._remove_message_from_buffer('permission-display-message')\n00926| end\n00927| M._rerender_part('permission-display-part')\n00928| end\n00929| end\n00930| \n00931| ---Event handler for question.asked events\n00932| ---Shows the question picker UI for the user to answer\n00933| ---@param properties OpencodeQuestionRequest Event properties\n00934| function M.on_question_asked(properties)\n00935| if not properties or not properties.id or not properties.questions then\n00936| return\n00937| end\n00938| \n00939| if not state.active_session or properties.sessionID ~= state.active_session.id then\n00940| return\n00941| end\n00942| \n00943| local question_window = require('opencode.ui.question_window')\n00944| question_window.show_question(properties)\n00945| end\n00946| \n00947| function M.on_file_edited(properties)\n00948| vim.cmd('checktime')\n00949| if config.hooks and config.hooks.on_file_edited then\n00950| pcall(config.hooks.on_file_edited, properties.file)\n00951| end\n00952| end\n00953| \n00954| ---@param properties RestorePointCreatedEvent\n00955| function M.on_restore_points(properties)\n00956| state.append('restore_points', properties.restore_point)\n00957| if not properties or not properties.restore_point or not properties.restore_point.from_snapshot_id then\n00958| return\n00959| end\n00960| local part = M._render_state:get_part_by_snapshot_id(properties.restore_point.from_snapshot_id)\n00961| if part then\n00962| M.on_part_updated({ part = part })\n00963| end\n00964| end\n00965| \n00966| ---Find part ID by call ID and message ID\n00967| ---Useful for finding a part for a permission\n00968| ---@param call_id string Call ID to search for\n00969| ---@param message_id string Message ID to check the parts of\n00970| ---@return string? part_id Part ID if found, nil otherwise\n00971| function M._find_part_by_call_id(call_id, message_id)\n00972| return M._render_state:get_part_by_call_id(call_id, message_id)\n00973| end\n00974| \n00975| ---Find the text part in a message\n00976| ---@param message OpencodeMessage The message containing the parts\n00977| ---@return string? text_part_id The ID of the text part\n00978| function M._find_text_part_for_message(message)\n00979| if not message or not message.parts then\n00980| return nil\n00981| end\n00982| \n00983| for _, part in ipairs(message.parts) do\n00984| if part.type == 'text' and not part.synthetic then\n00985| return part.id\n00986| end\n00987| end\n00988| \n00989| return nil\n00990| end\n00991| \n00992| ---Find the last part in a message\n00993| ---@param message OpencodeMessage The message containing the parts\n00994| ---@return string? last_part_id The ID of the last part\n00995| function M._get_last_part_for_message(message)\n00996| if not message or not message.parts or #message.parts == 0 then\n00997| return nil\n00998| end\n00999| \n01000| for i = #message.parts, 1, -1 do\n01001| local part = message.parts[i]\n01002| if part.type ~= 'step-start' and part.type ~= 'step-finish' and part.id then\n01003| return part.id\n01004| end\n01005| end\n01006| \n01007| return nil\n01008| end\n01009| \n01010| ---Get insertion point for an out-of-order part\n01011| ---@param part_id string The part ID to insert\n01012| ---@param message_id string The message ID the part belongs to\n01013| ---@return integer? insertion_line The line to insert at (1-indexed), or nil on error\n01014| function M._get_insertion_point_for_part(part_id, message_id)\n01015| local rendered_message = M._render_state:get_message(message_id)\n01016| if not rendered_message or not rendered_message.message then\n01017| return nil\n01018| end\n01019| \n01020| local message = rendered_message.message\n01021| \n01022| local insertion_line = rendered_message.line_end and (rendered_message.line_end + 1)\n01023| if not insertion_line then\n01024| return nil\n01025| end\n01026| \n01027| local current_part_index = nil\n01028| if message.parts then\n01029| for i, part in ipairs(message.parts) do\n01030| if part.id == part_id then\n01031| current_part_index = i\n01032| break\n01033| end\n01034| end\n01035| end\n01036| \n01037| if not current_part_index then\n01038| return insertion_line\n01039| end\n01040| \n01041| for i = current_part_index - 1, 1, -1 do\n01042| local prev_part = message.parts[i]\n01043| if prev_part and prev_part.id then\n01044| local prev_rendered = M._render_state:get_part(prev_part.id)\n01045| \n01046| if prev_rendered and prev_rendered.line_end then\n01047| return prev_rendered.line_end + 1\n01048| end\n01049| end\n01050| end\n01051| \n01052| return insertion_line\n01053| end\n01054| \n01055| ---Re-render existing part with current state\n01056| ---Used for permission updates and other dynamic changes\n01057| ---@param part_id string Part ID to re-render\n01058| function M._rerender_part(part_id)\n01059| local cached = M._render_state:get_part(part_id)\n01060| if not cached or not cached.part then\n01061| return\n01062| end\n01063| \n01064| local part = cached.part\n01065| local rendered_message = M._render_state:get_message(cached.message_id)\n01066| if not rendered_message or not rendered_message.message then\n01067| return\n01068| end\n01069| \n01070| local message = rendered_message.message\n01071| local last_part_id = M._get_last_part_for_message(message)\n01072| local is_last_part = (last_part_id == part_id)\n01073| local formatted = formatter.format_part(part, message, is_last_part)\n01074| \n01075| M._replace_part_in_buffer(part_id, formatted)\n01076| end\n01077| \n01078| ---Event handler for focus changes\n01079| ---Re-renders part associated with current permission for displaying global shortcuts or buffer-local ones\n01080| function M.on_focus_changed()\n01081| -- Check permission window first, fallback to state\n01082| local current_permission = permission_window.get_all_permissions()[1]\n01083| \n01084| if not current_permission then\n01085| return\n01086| end\n01087| \n01088| M._rerender_part('permission-display-part')\n01089| trigger_on_data_rendered()\n01090| end\n01091| \n01092| function M.on_session_changed(_, new, _)\n01093| M.reset()\n01094| if new then\n01095| M.render_full_session()\n01096| end\n01097| end\n01098| \n01099| ---Get all actions available at a specific line\n01100| ---@param line integer 0-indexed line number\n01101| ---@return table[] List of actions available at that line\n01102| function M.get_actions_for_line(line)\n01103| return M._render_state:get_actions_at_line(line)\n01104| end\n01105| \n01106| ---Update display stats from a single message\n01107| ---@param message OpencodeMessage\n01108| function M._update_stats_from_message(message)\n01109| if not state.current_model and message.info.providerID and message.info.providerID ~= '' then\n01110| state.current_model = message.info.providerID .. '/' .. message.info.modelID\n01111| end\n01112| \n01113| local tokens = message.info.tokens\n01114| if tokens and tokens.input > 0 then\n01115| state.tokens_count = tokens.input + tokens.output + tokens.cache.read + tokens.cache.write\n01116| end\n01117| \n01118| if message.info.cost and type(message.info.cost) == 'number' then\n01119| state.cost = message.info.cost\n01120| end\n01121| end\n01122| \n01123| ---Get rendered message by ID\n01124| ---@param message_id string Message ID\n01125| ---@return RenderedMessage|nil Rendered message or nil if not found\n01126| function M.get_rendered_message(message_id)\n01127| local rendered_msg = M._render_state:get_message(message_id)\n01128| if rendered_msg then\n01129| return rendered_msg\n01130| end\n01131| return nil\n01132| end\n01133| \n01134| return M\n01135| \n\n(End of file - total 1135 lines)\n","type":"text","id":"prt_bfaa078ae001zhO39Uz3en7btW"}}},{"type":"message.part.updated","properties":{"part":{"filename":"lua/opencode/ui/renderer.lua","messageID":"msg_bfaa078a2001RUYhlKZhSlyWeF","type":"file","id":"prt_bfaa078ae002XvKak6z08HB5LV","url":"file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua","mime":"text/plain","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD"}}},{"type":"message.part.updated","properties":{"part":{"messageID":"msg_bfaa078a2001RUYhlKZhSlyWeF","text":"can you use the question tool and ask me a question with an other choice","id":"prt_bfaa078a4001OxXpwZJxaFmqUo","type":"text","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD"}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","version":"1.1.31","slug":"calm-lagoon","id":"ses_4055f967bffeIHf3DnP0xntmxD","title":"New session - 2026-01-26T14:06:15.428Z","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","time":{"created":1769436375428,"updated":1769436379349}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"path":{"root":"/home/francis/Projects/_nvim/opencode.nvim","cwd":"/home/francis/Projects/_nvim/opencode.nvim"},"modelID":"gpt-4.1","providerID":"github-copilot","id":"msg_bfaa078e7001qc33BqBlqzHv8X","agent":"build","mode":"build","tokens":{"reasoning":0,"input":0,"cache":{"read":0,"write":0},"output":0},"parentID":"msg_bfaa078a2001RUYhlKZhSlyWeF","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","role":"assistant","cost":0,"time":{"created":1769436379367}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1769436379298},"id":"msg_bfaa078a2001RUYhlKZhSlyWeF","agent":"build","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","role":"user","model":{"modelID":"gpt-4.1","providerID":"github-copilot"},"summary":{"diffs":[]}}}},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","version":"1.1.31","title":"New session - 2026-01-26T14:06:15.428Z","slug":"calm-lagoon","id":"ses_4055f967bffeIHf3DnP0xntmxD","time":{"created":1769436375428,"updated":1769436379539},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"files":0,"additions":0}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.part.updated","properties":{"part":{"messageID":"msg_bfaa078e7001qc33BqBlqzHv8X","id":"prt_bfaa07ecf0011yakDSUtAyStvx","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","type":"step-start","snapshot":"56238d5c6bb499e9e265444377a201fba6b1a880"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfaa07ed3001RYplcvPxb660OT","type":"tool","tool":"question","state":{"raw":"","status":"pending","input":{}},"sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","messageID":"msg_bfaa078e7001qc33BqBlqzHv8X","callID":"call_IpPBuTk1riUXZDmDUDghZEpB"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"question.asked","properties":{"id":"que_bfaa0833a001jKE5x0BCsGLOYX","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","questions":[{"header":"Favorite color","question":"What is your favorite color?","options":[{"label":"Red","description":"The color red"},{"label":"Blue","description":"The color blue"},{"label":"Other","description":"Any color not listed"}]}],"tool":{"messageID":"msg_bfaa078e7001qc33BqBlqzHv8X","callID":"call_IpPBuTk1riUXZDmDUDghZEpB"}}},{"type":"message.part.updated","properties":{"part":{"id":"prt_bfaa07ed3001RYplcvPxb660OT","type":"tool","tool":"question","state":{"status":"running","input":{"questions":[{"header":"Favorite color","question":"What is your favorite color?","options":[{"label":"Red","description":"The color red"},{"label":"Blue","description":"The color blue"},{"label":"Other","description":"Any color not listed"}]}]},"time":{"start":1769436382010}},"sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","messageID":"msg_bfaa078e7001qc33BqBlqzHv8X","callID":"call_IpPBuTk1riUXZDmDUDghZEpB"}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"custom.emit_events.finished","properties":[]},{"type":"message.updated","properties":{"info":{"time":{"created":1769436379298},"id":"msg_bfaa078a2001RUYhlKZhSlyWeF","agent":"build","sessionID":"ses_4055f967bffeIHf3DnP0xntmxD","role":"user","model":{"modelID":"gpt-4.1","providerID":"github-copilot"},"summary":{"diffs":[],"title":"Question including Other option"}}}},{"type":"custom.emit_events.finished","properties":[]},{"type":"session.updated","properties":{"info":{"directory":"/home/francis/Projects/_nvim/opencode.nvim","version":"1.1.31","title":"Question about opencode/ui/renderer.lua","slug":"calm-lagoon","id":"ses_4055f967bffeIHf3DnP0xntmxD","time":{"created":1769436375428,"updated":1769436379539},"projectID":"29d43526f88157cd4edd071b899dd01f240b771b","summary":{"deletions":0,"files":0,"additions":0}}}},{"type":"custom.emit_events.finished","properties":[]}] +[ + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "version": "1.1.31", + "slug": "calm-lagoon", + "id": "ses_4055f967bffeIHf3DnP0xntmxD", + "title": "New session - 2026-01-26T14:06:15.428Z", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769436375428, "updated": 1769436375428 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769436379298 }, + "id": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "role": "user", + "model": { "modelID": "gpt-4.1", "providerID": "github-copilot" }, + "agent": "build" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "synthetic": true, + "messageID": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua\"}", + "type": "text", + "id": "prt_bfaa078a5001Pni4eaodfXL6o6" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "synthetic": true, + "messageID": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "text": "\n00001| local state = require('opencode.state')\n00002| local config = require('opencode.config')\n00003| local formatter = require('opencode.ui.formatter')\n00004| local output_window = require('opencode.ui.output_window')\n00005| local permission_window = require('opencode.ui.permission_window')\n00006| local Promise = require('opencode.promise')\n00007| local RenderState = require('opencode.ui.render_state')\n00008| \n00009| local M = {\n00010| _prev_line_count = 0,\n00011| _render_state = RenderState.new(),\n00012| _last_part_formatted = {\n00013| part_id = nil,\n00014| formatted_data = nil --[[@as Output|nil]],\n00015| },\n00016| }\n00017| \n00018| local trigger_on_data_rendered = require('opencode.util').debounce(function()\n00019| local cb_type = type(config.ui.output.rendering.on_data_rendered)\n00020| \n00021| if cb_type == 'boolean' then\n00022| return\n00023| end\n00024| \n00025| if not state.windows or not state.windows.output_buf or not state.windows.output_win then\n00026| return\n00027| end\n00028| \n00029| if cb_type == 'function' then\n00030| pcall(config.ui.output.rendering.on_data_rendered, state.windows.output_buf, state.windows.output_win)\n00031| elseif vim.fn.exists(':RenderMarkdown') > 0 then\n00032| vim.cmd(':RenderMarkdown')\n00033| elseif vim.fn.exists(':Markview') > 0 then\n00034| vim.cmd(':Markview render ' .. state.windows.output_buf)\n00035| end\n00036| end, config.ui.output.rendering.markdown_debounce_ms or 250)\n00037| \n00038| ---Reset renderer state\n00039| function M.reset()\n00040| M._prev_line_count = 0\n00041| M._render_state:reset()\n00042| M._last_part_formatted = { part_id = nil, formatted_data = nil }\n00043| \n00044| output_window.clear()\n00045| \n00046| state.messages = {}\n00047| state.last_user_message = nil\n00048| state.tokens_count = 0\n00049| \n00050| local permissions = state.pending_permissions or {}\n00051| if #permissions and state.api_client then\n00052| for _, permission in ipairs(permissions) do\n00053| require('opencode.api').permission_deny(permission)\n00054| end\n00055| end\n00056| permission_window.clear_all()\n00057| state.pending_permissions = {}\n00058| \n00059| trigger_on_data_rendered()\n00060| end\n00061| \n00062| ---Set up event subscriptions\n00063| ---@param subscribe? boolean false to unsubscribe\n00064| function M.setup_subscriptions(subscribe)\n00065| subscribe = subscribe == nil and true or subscribe\n00066| \n00067| if subscribe then\n00068| state.subscribe('is_opencode_focused', M.on_focus_changed)\n00069| state.subscribe('active_session', M.on_session_changed)\n00070| else\n00071| state.unsubscribe('is_opencode_focused', M.on_focus_changed)\n00072| state.unsubscribe('active_session', M.on_session_changed)\n00073| end\n00074| \n00075| if not state.event_manager then\n00076| return\n00077| end\n00078| \n00079| local event_subscriptions = {\n00080| { 'session.updated', M.on_session_updated },\n00081| { 'session.compacted', M.on_session_compacted },\n00082| { 'session.error', M.on_session_error },\n00083| { 'message.updated', M.on_message_updated },\n00084| { 'message.removed', M.on_message_removed },\n00085| { 'message.part.updated', M.on_part_updated },\n00086| { 'message.part.removed', M.on_part_removed },\n00087| { 'permission.updated', M.on_permission_updated },\n00088| { 'permission.asked', M.on_permission_updated },\n00089| { 'permission.replied', M.on_permission_replied },\n00090| { 'question.asked', M.on_question_asked },\n00091| { 'question.replied', M.clear_question_display },\n00092| { 'question.rejected', M.clear_question_display },\n00093| { 'file.edited', M.on_file_edited },\n00094| { 'custom.restore_point.created', M.on_restore_points },\n00095| { 'custom.emit_events.finished', M.on_emit_events_finished },\n00096| }\n00097| \n00098| for _, sub in ipairs(event_subscriptions) do\n00099| if subscribe then\n00100| state.event_manager:subscribe(sub[1], sub[2])\n00101| else\n00102| state.event_manager:unsubscribe(sub[1], sub[2])\n00103| end\n00104| end\n00105| end\n00106| \n00107| ---Clean up and teardown renderer. Unsubscribes from all events\n00108| function M.teardown()\n00109| M.setup_subscriptions(false)\n00110| M.reset()\n00111| end\n00112| \n00113| ---Fetch full session messages from server\n00114| ---@return Promise Promise resolving to list of OpencodeMessage\n00115| local function fetch_session()\n00116| local session = state.active_session\n00117| if not session or not session or session == '' then\n00118| return Promise.new():resolve(nil)\n00119| end\n00120| \n00121| state.last_user_message = nil\n00122| return require('opencode.session').get_messages(session)\n00123| end\n00124| \n00125| ---Request all of the session data from the opencode server and render it\n00126| ---@return Promise\n00127| function M.render_full_session()\n00128| if not output_window.mounted() or not state.api_client then\n00129| return Promise.new():resolve(nil)\n00130| end\n00131| \n00132| return fetch_session():and_then(M._render_full_session_data)\n00133| end\n00134| \n00135| function M._render_full_session_data(session_data)\n00136| M.reset()\n00137| \n00138| if not state.active_session or not state.messages then\n00139| return\n00140| end\n00141| \n00142| local revert_index = nil\n00143| \n00144| -- if we're loading a session and there's no currently selected model, set it\n00145| -- from the messages\n00146| local set_mode_from_messages = not state.current_model\n00147| \n00148| for i, msg in ipairs(session_data) do\n00149| if state.active_session.revert and state.active_session.revert.messageID == msg.info.id then\n00150| revert_index = i\n00151| end\n00152| \n00153| M.on_message_updated({ info = msg.info }, revert_index)\n00154| \n00155| for _, part in ipairs(msg.parts or {}) do\n00156| M.on_part_updated({ part = part }, revert_index)\n00157| end\n00158| end\n00159| \n00160| if revert_index then\n00161| M._write_formatted_data(formatter._format_revert_message(state.messages, revert_index))\n00162| end\n00163| \n00164| if set_mode_from_messages then\n00165| M._set_model_and_mode_from_messages()\n00166| end\n00167| M.scroll_to_bottom()\n00168| \n00169| if config.hooks and config.hooks.on_session_loaded then\n00170| pcall(config.hooks.on_session_loaded, state.active_session)\n00171| end\n00172| end\n00173| \n00174| ---Append permissions display as a fake part at the end\n00175| function M.render_permissions_display()\n00176| local fake_message = {\n00177| info = {\n00178| id = 'permission-display-message',\n00179| sessionID = state.active_session and state.active_session.id or '',\n00180| role = 'system',\n00181| },\n00182| parts = {},\n00183| }\n00184| M.on_message_updated(fake_message)\n00185| \n00186| local fake_part = {\n00187| id = 'permission-display-part',\n00188| messageID = 'permission-display-message',\n00189| sessionID = state.active_session and state.active_session.id or '',\n00190| type = 'permissions-display',\n00191| }\n00192| \n00193| local permissions = permission_window.get_all_permissions()\n00194| if #permissions > 0 then\n00195| M.on_part_updated({ part = fake_part })\n00196| end\n00197| end\n00198| \n00199| function M.clear_question_display()\n00200| local question_window = require('opencode.ui.question_window')\n00201| question_window.clear_question()\n00202| M._remove_part_from_buffer('question-display-part')\n00203| M._remove_message_from_buffer('question-display-message')\n00204| end\n00205| \n00206| ---Render question display as a fake part\n00207| function M.render_question_display()\n00208| local question_window = require('opencode.ui.question_window')\n00209| \n00210| local current_question = question_window._current_question\n00211| \n00212| if not question_window.has_question() or not current_question or not current_question.id then\n00213| M._remove_part_from_buffer('question-display-part')\n00214| M._remove_message_from_buffer('question-display-message')\n00215| return\n00216| end\n00217| \n00218| local message_id = 'question-display-message'\n00219| local part_id = 'question-display-part'\n00220| \n00221| local fake_message = {\n00222| info = {\n00223| id = message_id,\n00224| sessionID = state.active_session and state.active_session.id or '',\n00225| role = 'system',\n00226| },\n00227| parts = {},\n00228| }\n00229| M.on_message_updated(fake_message)\n00230| \n00231| local fake_part = {\n00232| id = part_id,\n00233| messageID = message_id,\n00234| sessionID = state.active_session and state.active_session.id or '',\n00235| type = 'questions-display',\n00236| }\n00237| \n00238| M.on_part_updated({ part = fake_part })\n00239| M._redraw()\n00240| M.scroll_to_bottom(true)\n00241| end\n00242| \n00243| function M._redraw()\n00244| if state.windows and state.windows.output_win and vim.api.nvim_win_is_valid(state.windows.output_win) then\n00245| vim.api.nvim_win_call(state.windows.output_win, function()\n00246| vim.cmd('redraw')\n00247| end)\n00248| end\n00249| end\n00250| \n00251| ---Render lines as the entire output buffer\n00252| ---@param lines any\n00253| function M.render_lines(lines)\n00254| local output = require('opencode.ui.output'):new()\n00255| output.lines = lines\n00256| M.render_output(output)\n00257| end\n00258| \n00259| ---Sets the entire output buffer based on output_data\n00260| ---@param output_data Output Output object from formatter\n00261| function M.render_output(output_data)\n00262| if not output_window.mounted() then\n00263| return\n00264| end\n00265| \n00266| local lines = output_data.lines or {}\n00267| \n00268| output_window.set_lines(lines)\n00269| output_window.clear_extmarks()\n00270| output_window.set_extmarks(output_data.extmarks)\n00271| M.scroll_to_bottom()\n00272| end\n00273| \n00274| ---Called when EventManager has finished emitting a batch of events\n00275| function M.on_emit_events_finished()\n00276| M.scroll_to_bottom()\n00277| end\n00278| \n00279| ---Find the most recently used model from the messages\n00280| function M._set_model_and_mode_from_messages()\n00281| if not state.messages then\n00282| return\n00283| end\n00284| \n00285| for i = #state.messages, 1, -1 do\n00286| local message = state.messages[i]\n00287| \n00288| if message and message.info then\n00289| if message.info.modelID and message.info.providerID then\n00290| state.current_model = message.info.providerID .. '/' .. message.info.modelID\n00291| if message.info.mode then\n00292| state.current_mode = message.info.mode\n00293| end\n00294| return\n00295| end\n00296| end\n00297| end\n00298| \n00299| -- we didn't find a model from any messages, set it to the default\n00300| require('opencode.core').initialize_current_model()\n00301| end\n00302| \n00303| ---Auto-scroll to bottom if user was already at bottom\n00304| ---Respects cursor position if user has scrolled up\n00305| ---@param force? boolean If true, scroll regardless of current position\n00306| function M.scroll_to_bottom(force)\n00307| if not state.windows or not state.windows.output_buf or not state.windows.output_win then\n00308| return\n00309| end\n00310| \n00311| if not vim.api.nvim_win_is_valid(state.windows.output_win) then\n00312| return\n00313| end\n00314| \n00315| local ok, line_count = pcall(vim.api.nvim_buf_line_count, state.windows.output_buf)\n00316| if not ok or line_count == 0 then\n00317| return\n00318| end\n00319| \n00320| local prev_line_count = M._prev_line_count or 0\n00321| \n00322| ---@cast line_count integer\n00323| M._prev_line_count = line_count\n00324| \n00325| trigger_on_data_rendered()\n00326| \n00327| -- Determine if we should scroll to bottom\n00328| local should_scroll = force == true\n00329| \n00330| if not should_scroll then\n00331| -- Always scroll on initial render\n00332| if prev_line_count == 0 then\n00333| should_scroll = true\n00334| -- Scroll if user is at bottom (respects manual scroll position)\n00335| elseif output_window.viewport_at_bottom then\n00336| should_scroll = true\n00337| end\n00338| end\n00339| \n00340| if should_scroll then\n00341| vim.api.nvim_win_set_cursor(state.windows.output_win, { line_count, 0 })\n00342| -- Use zb to position the cursor line at the bottom of the visible window\n00343| vim.api.nvim_win_call(state.windows.output_win, function()\n00344| vim.cmd('normal! zb')\n00345| end)\n00346| output_window.viewport_at_bottom = true\n00347| else\n00348| -- User has scrolled up, don't scroll\n00349| output_window.viewport_at_bottom = false\n00350| end\n00351| end\n00352| \n00353| ---Write data to output_buf, including normal text and extmarks\n00354| ---@param formatted_data Output Formatted data as Output object\n00355| ---@param part_id? string Optional part ID to store actions\n00356| ---@param start_line? integer Optional line to insert at (shifts content down). If nil, appends to end of buffer.\n00357| ---@return {line_start: integer, line_end: integer}? Range where data was written\n00358| function M._write_formatted_data(formatted_data, part_id, start_line)\n00359| if not state.windows or not state.windows.output_buf then\n00360| return\n00361| end\n00362| \n00363| local buf = state.windows.output_buf\n00364| local is_insertion = start_line ~= nil\n00365| local target_line = start_line or output_window.get_buf_line_count()\n00366| local new_lines = formatted_data.lines\n00367| local extmarks = formatted_data.extmarks\n00368| \n00369| if #new_lines == 0 or not buf then\n00370| return nil\n00371| end\n00372| \n00373| if is_insertion then\n00374| output_window.set_lines(new_lines, target_line, target_line)\n00375| else\n00376| local extra_newline = vim.tbl_extend('keep', {}, new_lines)\n00377| table.insert(extra_newline, '')\n00378| target_line = target_line - 1\n00379| output_window.set_lines(extra_newline, target_line)\n00380| end\n00381| \n00382| -- update actions and extmarks after the insertion because that may\n00383| -- adjust target_line (e.g. when we we're replacing the double newline at\n00384| -- the end)\n00385| \n00386| if part_id and formatted_data.actions then\n00387| M._render_state:add_actions(part_id, formatted_data.actions, target_line)\n00388| end\n00389| \n00390| output_window.set_extmarks(extmarks, target_line)\n00391| \n00392| return {\n00393| line_start = target_line,\n00394| line_end = target_line + #new_lines - 1,\n00395| }\n00396| end\n00397| \n00398| ---Insert new part, either at end of buffer or in the middle for out-of-order parts\n00399| ---@param part_id string Part ID\n00400| ---@param formatted_data Output Formatted data as Output object\n00401| ---@return boolean Success status\n00402| function M._insert_part_to_buffer(part_id, formatted_data)\n00403| local cached = M._render_state:get_part(part_id)\n00404| if not cached then\n00405| return false\n00406| end\n00407| \n00408| if #formatted_data.lines == 0 then\n00409| return true\n00410| end\n00411| \n00412| local is_current_message = state.current_message\n00413| and state.current_message.info\n00414| and state.current_message.info.id == cached.message_id\n00415| \n00416| if is_current_message then\n00417| -- NOTE: we're inserting a part for the current message, just add it to the end\n00418| \n00419| local range = M._write_formatted_data(formatted_data, part_id)\n00420| if not range then\n00421| return false\n00422| end\n00423| \n00424| M._render_state:set_part(cached.part, range.line_start, range.line_end)\n00425| \n00426| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00427| \n00428| return true\n00429| end\n00430| \n00431| -- NOTE: We're inserting a part for the first time for a previous message. We need to find\n00432| -- the insertion line (after the last part of this message or after the message header if\n00433| -- no parts).\n00434| local insertion_line = M._get_insertion_point_for_part(part_id, cached.message_id)\n00435| if not insertion_line then\n00436| return false\n00437| end\n00438| \n00439| local range = M._write_formatted_data(formatted_data, part_id, insertion_line)\n00440| if not range then\n00441| return false\n00442| end\n00443| \n00444| local line_count = #formatted_data.lines\n00445| M._render_state:shift_all(insertion_line, line_count)\n00446| \n00447| M._render_state:set_part(cached.part, range.line_start, range.line_end)\n00448| \n00449| return true\n00450| end\n00451| \n00452| ---Replace existing part in buffer\n00453| ---Adjusts line positions of subsequent parts if line count changes\n00454| ---@param part_id string Part ID\n00455| ---@param formatted_data Output Formatted data as Output object\n00456| ---@return boolean Success status\n00457| function M._replace_part_in_buffer(part_id, formatted_data)\n00458| local cached = M._render_state:get_part(part_id)\n00459| if not cached or not cached.line_start or not cached.line_end then\n00460| return false\n00461| end\n00462| \n00463| local new_lines = formatted_data.lines\n00464| local new_line_count = #new_lines\n00465| \n00466| local old_formatted = M._last_part_formatted\n00467| local can_optimize = old_formatted\n00468| and old_formatted.part_id == part_id\n00469| and old_formatted.formatted_data\n00470| and old_formatted.formatted_data.lines\n00471| \n00472| local lines_to_write = new_lines\n00473| local write_start_line = cached.line_start\n00474| \n00475| if can_optimize then\n00476| -- NOTE: This is an optimization to only replace the lines that are different\n00477| -- if we're replacing the most recently formatted part.\n00478| \n00479| ---@cast old_formatted { formatted_data: { lines: string[] } }\n00480| local old_lines = old_formatted.formatted_data.lines\n00481| local first_diff_line = nil\n00482| \n00483| -- Find the first line that's different\n00484| for i = 1, math.min(#old_lines, new_line_count) do\n00485| if old_lines[i] ~= new_lines[i] then\n00486| first_diff_line = i\n00487| break\n00488| end\n00489| end\n00490| \n00491| if not first_diff_line and new_line_count > #old_lines then\n00492| -- The old lines all matched but maybe there are more new lines\n00493| first_diff_line = #old_lines + 1\n00494| end\n00495| \n00496| if first_diff_line then\n00497| lines_to_write = vim.list_slice(new_lines, first_diff_line, new_line_count)\n00498| write_start_line = cached.line_start + first_diff_line - 1\n00499| elseif new_line_count == #old_lines then\n00500| -- Nothing was different, so we're done\n00501| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00502| return true\n00503| end\n00504| end\n00505| \n00506| M._render_state:clear_actions(part_id)\n00507| \n00508| output_window.clear_extmarks(cached.line_start - 1, cached.line_end + 1)\n00509| output_window.set_lines(lines_to_write, write_start_line, cached.line_end + 1)\n00510| \n00511| local new_line_end = cached.line_start + new_line_count - 1\n00512| \n00513| output_window.set_extmarks(formatted_data.extmarks, cached.line_start)\n00514| \n00515| if formatted_data.actions then\n00516| M._render_state:add_actions(part_id, formatted_data.actions, cached.line_start + 1)\n00517| end\n00518| \n00519| M._render_state:update_part_lines(part_id, cached.line_start, new_line_end)\n00520| \n00521| M._last_part_formatted = { part_id = part_id, formatted_data = formatted_data }\n00522| \n00523| return true\n00524| end\n00525| \n00526| ---Remove part from buffer and adjust subsequent line positions\n00527| ---@param part_id string Part ID\n00528| function M._remove_part_from_buffer(part_id)\n00529| local cached = M._render_state:get_part(part_id)\n00530| if not cached or not cached.line_start or not cached.line_end then\n00531| return\n00532| end\n00533| \n00534| if not state.windows or not state.windows.output_buf then\n00535| return\n00536| end\n00537| \n00538| output_window.clear_extmarks(cached.line_start - 1, cached.line_end)\n00539| output_window.set_lines({}, cached.line_start - 1, cached.line_end)\n00540| \n00541| M._render_state:remove_part(part_id)\n00542| end\n00543| \n00544| ---Remove message header from buffer and adjust subsequent line positions\n00545| ---@param message_id string Message ID\n00546| function M._remove_message_from_buffer(message_id)\n00547| local cached = M._render_state:get_message(message_id)\n00548| if not cached or not cached.line_start or not cached.line_end then\n00549| return\n00550| end\n00551| \n00552| if not state.windows or not state.windows.output_buf then\n00553| return\n00554| end\n00555| \n00556| output_window.clear_extmarks(cached.line_start - 1, cached.line_end)\n00557| output_window.set_lines({}, cached.line_start - 1, cached.line_end)\n00558| \n00559| M._render_state:remove_message(message_id)\n00560| end\n00561| \n00562| ---Adds a message (most likely just a header) to the buffer\n00563| ---@param message OpencodeMessage Message to add\n00564| function M._add_message_to_buffer(message)\n00565| local header_data = formatter.format_message_header(message)\n00566| local range = M._write_formatted_data(header_data)\n00567| \n00568| if range then\n00569| M._render_state:set_message(message, range.line_start, range.line_end)\n00570| end\n00571| end\n00572| \n00573| ---Replace existing message header in buffer\n00574| ---@param message_id string Message ID\n00575| ---@param formatted_data Output Formatted header as Output object\n00576| ---@return boolean Success status\n00577| function M._replace_message_in_buffer(message_id, formatted_data)\n00578| local cached = M._render_state:get_message(message_id)\n00579| if not cached or not cached.line_start or not cached.line_end then\n00580| return false\n00581| end\n00582| \n00583| local new_lines = formatted_data.lines\n00584| local new_line_count = #new_lines\n00585| \n00586| output_window.clear_extmarks(cached.line_start, cached.line_end + 1)\n00587| output_window.set_lines(new_lines, cached.line_start, cached.line_end + 1)\n00588| output_window.set_extmarks(formatted_data.extmarks, cached.line_start)\n00589| \n00590| local old_line_end = cached.line_end\n00591| local new_line_end = cached.line_start + new_line_count - 1\n00592| \n00593| M._render_state:set_message(cached.message, cached.line_start, new_line_end)\n00594| \n00595| local delta = new_line_end - old_line_end\n00596| if delta ~= 0 then\n00597| M._render_state:shift_all(old_line_end + 1, delta)\n00598| end\n00599| \n00600| return true\n00601| end\n00602| \n00603| ---Event handler for message.updated events\n00604| ---Creates new message or updates existing message info\n00605| ---@param message {info: MessageInfo} Event properties\n00606| ---@param revert_index? integer Revert index in session, if applicable\n00607| function M.on_message_updated(message, revert_index)\n00608| if not state.active_session or not state.messages then\n00609| return\n00610| end\n00611| \n00612| local msg = message --[[@as OpencodeMessage]]\n00613| if not msg or not msg.info or not msg.info.id or not msg.info.sessionID then\n00614| return\n00615| end\n00616| \n00617| if state.active_session.id ~= msg.info.sessionID then\n00618| ---@TODO This is probably a child session message, handle differently?\n00619| -- vim.notify('Session id does not match, discarding message: ' .. vim.inspect(message), vim.log.levels.WARN)\n00620| return\n00621| end\n00622| \n00623| local rendered_message = M._render_state:get_message(msg.info.id)\n00624| local found_msg = rendered_message and rendered_message.message\n00625| \n00626| if revert_index then\n00627| if not found_msg then\n00628| table.insert(state.messages, msg)\n00629| end\n00630| M._render_state:set_message(msg, 0, 0)\n00631| return\n00632| end\n00633| \n00634| if found_msg then\n00635| local error_changed = not vim.deep_equal(found_msg.info.error, msg.info.error)\n00636| \n00637| found_msg.info = msg.info\n00638| \n00639| --- NOTE: error handling is a bit messy because errors come in on messages\n00640| --- but we want to display the error at the end. In this case, we an error\n00641| --- was added to this message. We find the last part and re-render it to\n00642| --- display the message. If there are no parts, we'll re-render the message\n00643| \n00644| if error_changed and not revert_index then\n00645| local last_part_id = M._get_last_part_for_message(found_msg)\n00646| if last_part_id then\n00647| M._rerender_part(last_part_id)\n00648| else\n00649| local header_data = formatter.format_message_header(found_msg)\n00650| M._replace_message_in_buffer(msg.info.id, header_data)\n00651| end\n00652| end\n00653| else\n00654| table.insert(state.messages, msg)\n00655| \n00656| M._add_message_to_buffer(msg)\n00657| \n00658| state.current_message = msg\n00659| if message.info.role == 'user' then\n00660| state.last_user_message = msg\n00661| end\n00662| end\n00663| \n00664| M._update_stats_from_message(msg)\n00665| end\n00666| \n00667| ---Event handler for message.part.updated events\n00668| ---Inserts new parts or replaces existing parts in buffer\n00669| ---@param properties {part: OpencodeMessagePart} Event properties\n00670| ---@param revert_index? integer Revert index in session, if applicable\n00671| function M.on_part_updated(properties, revert_index)\n00672| if not properties or not properties.part or not state.active_session then\n00673| return\n00674| end\n00675| \n00676| local part = properties.part\n00677| if not part.id or not part.messageID or not part.sessionID then\n00678| return\n00679| end\n00680| \n00681| if state.active_session.id ~= part.sessionID then\n00682| ---@TODO This is probably a child session part, handle differently?\n00683| -- vim.notify('Session id does not match, discarding part: ' .. vim.inspect(part), vim.log.levels.WARN)\n00684| return\n00685| end\n00686| \n00687| local rendered_message = M._render_state:get_message(part.messageID)\n00688| if not rendered_message or not rendered_message.message then\n00689| vim.notify('Could not find message for part: ' .. vim.inspect(part), vim.log.levels.WARN)\n00690| return\n00691| end\n00692| \n00693| local message = rendered_message.message\n00694| \n00695| message.parts = message.parts or {}\n00696| \n00697| local part_data = M._render_state:get_part(part.id)\n00698| local is_new_part = not part_data\n00699| \n00700| local prev_last_part_id = M._get_last_part_for_message(message)\n00701| local is_last_part = is_new_part or (prev_last_part_id == part.id)\n00702| \n00703| if is_new_part then\n00704| table.insert(message.parts, part)\n00705| else\n00706| for i = #message.parts, 1, -1 do\n00707| if message.parts[i].id == part.id then\n00708| message.parts[i] = part\n00709| break\n00710| end\n00711| end\n00712| end\n00713| \n00714| if part.type == 'step-start' or part.type == 'step-finish' then\n00715| return\n00716| end\n00717| \n00718| if is_new_part then\n00719| M._render_state:set_part(part)\n00720| else\n00721| local rendered_part = M._render_state:update_part_data(part)\n00722| -- NOTE: This isn't the first time we've seen the part but we haven't rendered it previously\n00723| -- so try and render it this time by setting is_new_part = true (otherwise we'd call\n00724| -- _replace_message_in_buffer and it wouldn't do anything because the part hasn't been rendered)\n00725| if not rendered_part or (not rendered_part.line_start and not rendered_part.line_end) then\n00726| is_new_part = true\n00727| end\n00728| end\n00729| \n00730| local formatted = formatter.format_part(part, message, is_last_part)\n00731| \n00732| if revert_index and is_new_part then\n00733| return\n00734| end\n00735| \n00736| if is_new_part then\n00737| M._insert_part_to_buffer(part.id, formatted)\n00738| \n00739| if message.info.error then\n00740| --- NOTE: More error display code. As mentioned above, errors come in on messages\n00741| --- but we want to display them after parts so we tack the error onto the last\n00742| --- part. When a part is added and there's an error, we need to rerender\n00743| --- previous last part so it doesn't also display the message. If there was no previous\n00744| --- part, then we need to rerender the header so it doesn't display the error\n00745| \n00746| if not prev_last_part_id then\n00747| -- no previous part, we're the first part, re-render the message header\n00748| -- so it doesn't also display the error\n00749| local header_data = formatter.format_message_header(message)\n00750| M._replace_message_in_buffer(part.messageID, header_data)\n00751| elseif prev_last_part_id ~= part.id then\n00752| M._rerender_part(prev_last_part_id)\n00753| end\n00754| end\n00755| else\n00756| M._replace_part_in_buffer(part.id, formatted)\n00757| end\n00758| \n00759| if (part.type == 'file' or part.type == 'agent') and part.source then\n00760| -- we have a mention, we need to rerender the early part to highlight\n00761| -- the mention.\n00762| local text_part_id = M._find_text_part_for_message(message)\n00763| if text_part_id then\n00764| M._rerender_part(text_part_id)\n00765| end\n00766| end\n00767| end\n00768| \n00769| ---Event handler for message.part.removed events\n00770| ---@param properties {sessionID: string, messageID: string, partID: string} Event properties\n00771| function M.on_part_removed(properties)\n00772| if not properties then\n00773| return\n00774| end\n00775| \n00776| local part_id = properties.partID\n00777| if not part_id then\n00778| return\n00779| end\n00780| \n00781| local cached = M._render_state:get_part(part_id)\n00782| if cached and cached.message_id then\n00783| local rendered_message = M._render_state:get_message(cached.message_id)\n00784| if rendered_message and rendered_message.message then\n00785| local message = rendered_message.message\n00786| if message.parts then\n00787| for i, part in ipairs(message.parts) do\n00788| if part.id == part_id then\n00789| table.remove(message.parts, i)\n00790| break\n00791| end\n00792| end\n00793| end\n00794| end\n00795| end\n00796| \n00797| M._remove_part_from_buffer(part_id)\n00798| end\n00799| \n00800| ---Event handler for message.removed events\n00801| ---Removes message and all its parts from buffer\n00802| ---@param properties {sessionID: string, messageID: string} Event properties\n00803| function M.on_message_removed(properties)\n00804| if not properties or not state.messages then\n00805| return\n00806| end\n00807| \n00808| local message_id = properties.messageID\n00809| if not message_id then\n00810| return\n00811| end\n00812| \n00813| local rendered_message = M._render_state:get_message(message_id)\n00814| if not rendered_message or not rendered_message.message then\n00815| return\n00816| end\n00817| \n00818| local message = rendered_message.message\n00819| for _, part in ipairs(message.parts or {}) do\n00820| if part.id then\n00821| M._remove_part_from_buffer(part.id)\n00822| end\n00823| end\n00824| \n00825| M._remove_message_from_buffer(message_id)\n00826| \n00827| for i, msg in ipairs(state.messages or {}) do\n00828| if msg.info.id == message_id then\n00829| table.remove(state.messages, i)\n00830| break\n00831| end\n00832| end\n00833| end\n00834| \n00835| ---Event handler for session.compacted events\n00836| ---@param properties {sessionID: string} Event properties\n00837| function M.on_session_compacted(properties)\n00838| vim.notify('Session has been compacted')\n00839| end\n00840| \n00841| ---Event handler for session.updated events\n00842| ---@param properties {info: Session}\n00843| function M.on_session_updated(properties)\n00844| if not properties or not properties.info or not state.active_session then\n00845| return\n00846| end\n00847| if not vim.deep_equal(state.active_session.revert, properties.info.revert) then\n00848| state.active_session.revert = properties.info.revert\n00849| M._render_full_session_data(state.messages)\n00850| end\n00851| end\n00852| \n00853| ---Event handler for session.error events\n00854| ---@param properties {sessionID: string, error: table} Event properties\n00855| function M.on_session_error(properties)\n00856| if not properties or not properties.error then\n00857| return\n00858| end\n00859| \n00860| -- NOTE: we're handling message errors so session errors seem duplicative\n00861| if config.debug.enabled then\n00862| vim.notify('Session error: ' .. vim.inspect(properties.error))\n00863| end\n00864| end\n00865| \n00866| ---Event handler for permission.updated events\n00867| ---Re-renders part that requires permission and adds to permission window\n00868| ---@param permission OpencodePermission Event properties\n00869| function M.on_permission_updated(permission)\n00870| local tool = permission.tool\n00871| \n00872| ---@TODO this is for backward compatibility, remove later\n00873| local callID = tool and tool.callID or permission.callID\n00874| local messageID = tool and tool.messageID or permission.messageID\n00875| \n00876| if not permission or not messageID or not callID then\n00877| return\n00878| end\n00879| \n00880| -- Add permission to pending queue\n00881| if not state.pending_permissions then\n00882| state.pending_permissions = {}\n00883| end\n00884| \n00885| -- Check if permission already exists in queue\n00886| local existing_index = nil\n00887| for i, existing in ipairs(state.pending_permissions) do\n00888| if existing.id == permission.id then\n00889| existing_index = i\n00890| break\n00891| end\n00892| end\n00893| \n00894| local permissions = vim.deepcopy(state.pending_permissions)\n00895| if existing_index then\n00896| permissions[existing_index] = permission\n00897| else\n00898| table.insert(permissions, permission)\n00899| end\n00900| state.pending_permissions = permissions\n00901| \n00902| permission_window.add_permission(permission)\n00903| \n00904| M.render_permissions_display()\n00905| \n00906| M._rerender_part('permission-display-part')\n00907| M.scroll_to_bottom()\n00908| end\n00909| \n00910| ---Event handler for permission.replied events\n00911| ---Re-renders part after permission is resolved and removes from window\n00912| ---@param properties {sessionID: string, permissionID?: string,requestID?: string, response: string}|{} Event properties\n00913| function M.on_permission_replied(properties)\n00914| if not properties then\n00915| return\n00916| end\n00917| \n00918| local permission_id = properties.permissionID or properties.requestID\n00919| \n00920| if permission_id then\n00921| permission_window.remove_permission(permission_id)\n00922| state.pending_permissions = vim.deepcopy(permission_window.get_all_permissions())\n00923| if #state.pending_permissions == 0 then\n00924| M._remove_part_from_buffer('permission-display-part')\n00925| M._remove_message_from_buffer('permission-display-message')\n00926| end\n00927| M._rerender_part('permission-display-part')\n00928| end\n00929| end\n00930| \n00931| ---Event handler for question.asked events\n00932| ---Shows the question picker UI for the user to answer\n00933| ---@param properties OpencodeQuestionRequest Event properties\n00934| function M.on_question_asked(properties)\n00935| if not properties or not properties.id or not properties.questions then\n00936| return\n00937| end\n00938| \n00939| if not state.active_session or properties.sessionID ~= state.active_session.id then\n00940| return\n00941| end\n00942| \n00943| local question_window = require('opencode.ui.question_window')\n00944| question_window.show_question(properties)\n00945| end\n00946| \n00947| function M.on_file_edited(properties)\n00948| vim.cmd('checktime')\n00949| if config.hooks and config.hooks.on_file_edited then\n00950| pcall(config.hooks.on_file_edited, properties.file)\n00951| end\n00952| end\n00953| \n00954| ---@param properties RestorePointCreatedEvent\n00955| function M.on_restore_points(properties)\n00956| state.append('restore_points', properties.restore_point)\n00957| if not properties or not properties.restore_point or not properties.restore_point.from_snapshot_id then\n00958| return\n00959| end\n00960| local part = M._render_state:get_part_by_snapshot_id(properties.restore_point.from_snapshot_id)\n00961| if part then\n00962| M.on_part_updated({ part = part })\n00963| end\n00964| end\n00965| \n00966| ---Find part ID by call ID and message ID\n00967| ---Useful for finding a part for a permission\n00968| ---@param call_id string Call ID to search for\n00969| ---@param message_id string Message ID to check the parts of\n00970| ---@return string? part_id Part ID if found, nil otherwise\n00971| function M._find_part_by_call_id(call_id, message_id)\n00972| return M._render_state:get_part_by_call_id(call_id, message_id)\n00973| end\n00974| \n00975| ---Find the text part in a message\n00976| ---@param message OpencodeMessage The message containing the parts\n00977| ---@return string? text_part_id The ID of the text part\n00978| function M._find_text_part_for_message(message)\n00979| if not message or not message.parts then\n00980| return nil\n00981| end\n00982| \n00983| for _, part in ipairs(message.parts) do\n00984| if part.type == 'text' and not part.synthetic then\n00985| return part.id\n00986| end\n00987| end\n00988| \n00989| return nil\n00990| end\n00991| \n00992| ---Find the last part in a message\n00993| ---@param message OpencodeMessage The message containing the parts\n00994| ---@return string? last_part_id The ID of the last part\n00995| function M._get_last_part_for_message(message)\n00996| if not message or not message.parts or #message.parts == 0 then\n00997| return nil\n00998| end\n00999| \n01000| for i = #message.parts, 1, -1 do\n01001| local part = message.parts[i]\n01002| if part.type ~= 'step-start' and part.type ~= 'step-finish' and part.id then\n01003| return part.id\n01004| end\n01005| end\n01006| \n01007| return nil\n01008| end\n01009| \n01010| ---Get insertion point for an out-of-order part\n01011| ---@param part_id string The part ID to insert\n01012| ---@param message_id string The message ID the part belongs to\n01013| ---@return integer? insertion_line The line to insert at (1-indexed), or nil on error\n01014| function M._get_insertion_point_for_part(part_id, message_id)\n01015| local rendered_message = M._render_state:get_message(message_id)\n01016| if not rendered_message or not rendered_message.message then\n01017| return nil\n01018| end\n01019| \n01020| local message = rendered_message.message\n01021| \n01022| local insertion_line = rendered_message.line_end and (rendered_message.line_end + 1)\n01023| if not insertion_line then\n01024| return nil\n01025| end\n01026| \n01027| local current_part_index = nil\n01028| if message.parts then\n01029| for i, part in ipairs(message.parts) do\n01030| if part.id == part_id then\n01031| current_part_index = i\n01032| break\n01033| end\n01034| end\n01035| end\n01036| \n01037| if not current_part_index then\n01038| return insertion_line\n01039| end\n01040| \n01041| for i = current_part_index - 1, 1, -1 do\n01042| local prev_part = message.parts[i]\n01043| if prev_part and prev_part.id then\n01044| local prev_rendered = M._render_state:get_part(prev_part.id)\n01045| \n01046| if prev_rendered and prev_rendered.line_end then\n01047| return prev_rendered.line_end + 1\n01048| end\n01049| end\n01050| end\n01051| \n01052| return insertion_line\n01053| end\n01054| \n01055| ---Re-render existing part with current state\n01056| ---Used for permission updates and other dynamic changes\n01057| ---@param part_id string Part ID to re-render\n01058| function M._rerender_part(part_id)\n01059| local cached = M._render_state:get_part(part_id)\n01060| if not cached or not cached.part then\n01061| return\n01062| end\n01063| \n01064| local part = cached.part\n01065| local rendered_message = M._render_state:get_message(cached.message_id)\n01066| if not rendered_message or not rendered_message.message then\n01067| return\n01068| end\n01069| \n01070| local message = rendered_message.message\n01071| local last_part_id = M._get_last_part_for_message(message)\n01072| local is_last_part = (last_part_id == part_id)\n01073| local formatted = formatter.format_part(part, message, is_last_part)\n01074| \n01075| M._replace_part_in_buffer(part_id, formatted)\n01076| end\n01077| \n01078| ---Event handler for focus changes\n01079| ---Re-renders part associated with current permission for displaying global shortcuts or buffer-local ones\n01080| function M.on_focus_changed()\n01081| -- Check permission window first, fallback to state\n01082| local current_permission = permission_window.get_all_permissions()[1]\n01083| \n01084| if not current_permission then\n01085| return\n01086| end\n01087| \n01088| M._rerender_part('permission-display-part')\n01089| trigger_on_data_rendered()\n01090| end\n01091| \n01092| function M.on_session_changed(_, new, _)\n01093| M.reset()\n01094| if new then\n01095| M.render_full_session()\n01096| end\n01097| end\n01098| \n01099| ---Get all actions available at a specific line\n01100| ---@param line integer 0-indexed line number\n01101| ---@return table[] List of actions available at that line\n01102| function M.get_actions_for_line(line)\n01103| return M._render_state:get_actions_at_line(line)\n01104| end\n01105| \n01106| ---Update display stats from a single message\n01107| ---@param message OpencodeMessage\n01108| function M._update_stats_from_message(message)\n01109| if not state.current_model and message.info.providerID and message.info.providerID ~= '' then\n01110| state.current_model = message.info.providerID .. '/' .. message.info.modelID\n01111| end\n01112| \n01113| local tokens = message.info.tokens\n01114| if tokens and tokens.input > 0 then\n01115| state.tokens_count = tokens.input + tokens.output + tokens.cache.read + tokens.cache.write\n01116| end\n01117| \n01118| if message.info.cost and type(message.info.cost) == 'number' then\n01119| state.cost = message.info.cost\n01120| end\n01121| end\n01122| \n01123| ---Get rendered message by ID\n01124| ---@param message_id string Message ID\n01125| ---@return RenderedMessage|nil Rendered message or nil if not found\n01126| function M.get_rendered_message(message_id)\n01127| local rendered_msg = M._render_state:get_message(message_id)\n01128| if rendered_msg then\n01129| return rendered_msg\n01130| end\n01131| return nil\n01132| end\n01133| \n01134| return M\n01135| \n\n(End of file - total 1135 lines)\n", + "type": "text", + "id": "prt_bfaa078ae001zhO39Uz3en7btW" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "filename": "lua/opencode/ui/renderer.lua", + "messageID": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "type": "file", + "id": "prt_bfaa078ae002XvKak6z08HB5LV", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/lua/opencode/ui/renderer.lua", + "mime": "text/plain", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "messageID": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "text": "can you use the question tool and ask me a question with an other choice", + "id": "prt_bfaa078a4001OxXpwZJxaFmqUo", + "type": "text", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD" + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "version": "1.1.31", + "slug": "calm-lagoon", + "id": "ses_4055f967bffeIHf3DnP0xntmxD", + "title": "New session - 2026-01-26T14:06:15.428Z", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "time": { "created": 1769436375428, "updated": 1769436379349 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "path": { + "root": "/home/francis/Projects/_nvim/opencode.nvim", + "cwd": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "modelID": "gpt-4.1", + "providerID": "github-copilot", + "id": "msg_bfaa078e7001qc33BqBlqzHv8X", + "agent": "build", + "mode": "build", + "tokens": { "reasoning": 0, "input": 0, "cache": { "read": 0, "write": 0 }, "output": 0 }, + "parentID": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "role": "assistant", + "cost": 0, + "time": { "created": 1769436379367 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769436379298 }, + "id": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "agent": "build", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "role": "user", + "model": { "modelID": "gpt-4.1", "providerID": "github-copilot" }, + "summary": { "diffs": [] } + } + } + }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "version": "1.1.31", + "title": "New session - 2026-01-26T14:06:15.428Z", + "slug": "calm-lagoon", + "id": "ses_4055f967bffeIHf3DnP0xntmxD", + "time": { "created": 1769436375428, "updated": 1769436379539 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "files": 0, "additions": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.part.updated", + "properties": { + "part": { + "messageID": "msg_bfaa078e7001qc33BqBlqzHv8X", + "id": "prt_bfaa07ecf0011yakDSUtAyStvx", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "type": "step-start", + "snapshot": "56238d5c6bb499e9e265444377a201fba6b1a880" + } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfaa07ed3001RYplcvPxb660OT", + "type": "tool", + "tool": "question", + "state": { "raw": "", "status": "pending", "input": {} }, + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "messageID": "msg_bfaa078e7001qc33BqBlqzHv8X", + "callID": "call_IpPBuTk1riUXZDmDUDghZEpB" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "question.asked", + "properties": { + "id": "que_bfaa0833a001jKE5x0BCsGLOYX", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "questions": [ + { + "header": "Favorite color", + "question": "What is your favorite color?", + "options": [ + { "label": "Red", "description": "The color red" }, + { "label": "Blue", "description": "The color blue" }, + { "label": "Other", "description": "Any color not listed" } + ] + } + ], + "tool": { "messageID": "msg_bfaa078e7001qc33BqBlqzHv8X", "callID": "call_IpPBuTk1riUXZDmDUDghZEpB" } + } + }, + { + "type": "message.part.updated", + "properties": { + "part": { + "id": "prt_bfaa07ed3001RYplcvPxb660OT", + "type": "tool", + "tool": "question", + "state": { + "status": "running", + "input": { + "questions": [ + { + "header": "Favorite color", + "question": "What is your favorite color?", + "options": [ + { "label": "Red", "description": "The color red" }, + { "label": "Blue", "description": "The color blue" }, + { "label": "Other", "description": "Any color not listed" } + ] + } + ] + }, + "time": { "start": 1769436382010 } + }, + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "messageID": "msg_bfaa078e7001qc33BqBlqzHv8X", + "callID": "call_IpPBuTk1riUXZDmDUDghZEpB" + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "message.updated", + "properties": { + "info": { + "time": { "created": 1769436379298 }, + "id": "msg_bfaa078a2001RUYhlKZhSlyWeF", + "agent": "build", + "sessionID": "ses_4055f967bffeIHf3DnP0xntmxD", + "role": "user", + "model": { "modelID": "gpt-4.1", "providerID": "github-copilot" }, + "summary": { "diffs": [], "title": "Question including Other option" } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] }, + { + "type": "session.updated", + "properties": { + "info": { + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "version": "1.1.31", + "title": "Question about opencode/ui/renderer.lua", + "slug": "calm-lagoon", + "id": "ses_4055f967bffeIHf3DnP0xntmxD", + "time": { "created": 1769436375428, "updated": 1769436379539 }, + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "summary": { "deletions": 0, "files": 0, "additions": 0 } + } + } + }, + { "type": "custom.emit_events.finished", "properties": [] } +] diff --git a/tests/data/reasoning.expected.json b/tests/data/reasoning.expected.json index 65db68da..178a92e8 100644 --- a/tests/data/reasoning.expected.json +++ b/tests/data/reasoning.expected.json @@ -1 +1,290 @@ -{"actions":[],"timestamp":1766432126,"extmarks":[[1,1,0,{"virt_text_hide":false,"virt_text_pos":"win_col","virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-12-17 19:33:20)","OpencodeHint"],[" [msg_reason_user1]","OpencodeHint"]],"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false,"right_gravity":true}],[2,2,0,{"virt_text_hide":false,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true}],[3,3,0,{"virt_text_hide":false,"virt_text_pos":"win_col","virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_win_col":-3,"priority":4096,"virt_text_repeat_linebreak":true,"right_gravity":true}],[4,6,0,{"virt_text_hide":false,"virt_text_pos":"win_col","virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-12-17 19:33:20)","OpencodeHint"],[" [msg_reason_asst1]","OpencodeHint"]],"ns_id":3,"virt_text_win_col":-3,"priority":10,"virt_text_repeat_linebreak":false,"right_gravity":true}],[5,8,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[6,9,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[7,10,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[8,11,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[9,12,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[10,13,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[11,14,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[12,15,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[13,16,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[14,17,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}],[15,18,0,{"virt_text_hide":false,"virt_text_repeat_linebreak":true,"ns_id":3,"right_gravity":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"line_hl_group":"OpencodeReasoningText"}]],"lines":["----","","","Explain your plan, then provide the final title.","","----","","","**󰧑 Reasoning (2025-12-17 19:33:20 - 2025-12-17 19:33:21)** ","","The user is asking me to explain my plan and then provide a final title. I need to think through what they're asking for. It seems like they want me to demonstrate reasoning capabilities - showing my thought process before giving an answer. Let me break this down into steps.","","1. First, I should understand what kind of title they want - is this for a project, document, or something else?","2. Since they didn't specify the context, I should provide a general approach","3. I'll demonstrate my reasoning process transparently","4. Then I'll give them a concrete example title","","Given that this appears to be related to the OpenCode project (based on the file structure I can see), I should provide a title that reflects the nature of this AI coding assistant tool.","","Based on my reasoning above, here's my plan:","","1. Acknowledge the request for demonstrating reasoning","2. Show transparency in my thought process","3. Provide context-appropriate suggestions","","For the title, considering this is the OpenCode Neovim plugin - an AI-powered coding assistant - a good title would be:","","**\"OpenCode.nvim: AI-Powered Coding Assistant for Neovim\"**","","This title clearly identifies what the tool is (OpenCode), the platform it works with (Neovim), and its primary function (AI-powered coding assistance).","",""]} \ No newline at end of file +{ + "actions": [], + "timestamp": 1766432126, + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-12-17 19:33:20)", "OpencodeHint"], + [" [msg_reason_user1]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 4, + 6, + 0, + { + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-12-17 19:33:20)", "OpencodeHint"], + [" [msg_reason_asst1]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 5, + 8, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 6, + 9, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 7, + 10, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 8, + 11, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 9, + 12, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 10, + 13, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 11, + 14, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 12, + 15, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 13, + 16, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 14, + 17, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ], + [ + 15, + 18, + 0, + { + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "ns_id": 3, + "right_gravity": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "line_hl_group": "OpencodeReasoningText" + } + ] + ], + "lines": [ + "----", + "", + "", + "Explain your plan, then provide the final title.", + "", + "----", + "", + "", + "**󰧑 Reasoning (2025-12-17 19:33:20 - 2025-12-17 19:33:21)** ", + "", + "The user is asking me to explain my plan and then provide a final title. I need to think through what they're asking for. It seems like they want me to demonstrate reasoning capabilities - showing my thought process before giving an answer. Let me break this down into steps.", + "", + "1. First, I should understand what kind of title they want - is this for a project, document, or something else?", + "2. Since they didn't specify the context, I should provide a general approach", + "3. I'll demonstrate my reasoning process transparently", + "4. Then I'll give them a concrete example title", + "", + "Given that this appears to be related to the OpenCode project (based on the file structure I can see), I should provide a title that reflects the nature of this AI coding assistant tool.", + "", + "Based on my reasoning above, here's my plan:", + "", + "1. Acknowledge the request for demonstrating reasoning", + "2. Show transparency in my thought process", + "3. Provide context-appropriate suggestions", + "", + "For the title, considering this is the OpenCode Neovim plugin - an AI-powered coding assistant - a good title would be:", + "", + "**\"OpenCode.nvim: AI-Powered Coding Assistant for Neovim\"**", + "", + "This title clearly identifies what the tool is (OpenCode), the platform it works with (Neovim), and its primary function (AI-powered coding assistance).", + "", + "" + ] +} diff --git a/tests/data/redo-all.expected.json b/tests/data/redo-all.expected.json index db0265d9..7996a65d 100644 --- a/tests/data/redo-all.expected.json +++ b/tests/data/redo-all.expected.json @@ -1 +1,1060 @@ -{"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 15:20:02)","OpencodeHint"],[" [msg_a0234c0b7001y2o9S1jMaNVZar]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[4,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[5,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[6,8,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:04)","OpencodeHint"],[" [msg_a0234c7960011LTxTvD94hfWCi]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[7,12,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[8,13,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[9,14,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[10,15,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffDelete","hl_eol":true,"virt_text":[["-","OpencodeDiffDelete"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":16,"virt_text_repeat_linebreak":false,"right_gravity":true}],[11,15,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[12,16,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffAdd","hl_eol":true,"virt_text":[["+","OpencodeDiffAdd"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":17,"virt_text_repeat_linebreak":false,"right_gravity":true}],[13,16,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[14,17,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[15,18,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[16,19,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[17,20,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[18,25,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:09)","OpencodeHint"],[" [msg_a0234d8fb001SXyngLjuKSuxOY]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[19,30,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 15:20:11)","OpencodeHint"],[" [msg_a0234e308001SKl5bQUibp5gtI]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[20,31,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[21,32,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[22,35,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:11)","OpencodeHint"],[" [msg_a0234e31f001m4EsQdPmY3PTtS]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[23,42,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:16)","OpencodeHint"],[" [msg_a0234f482001PQbMjWc6W8s0eF]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[24,46,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[25,47,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[26,48,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[27,49,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffDelete","hl_eol":true,"virt_text":[["-","OpencodeDiffDelete"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":50,"virt_text_repeat_linebreak":false,"right_gravity":true}],[28,49,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[29,50,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffAdd","hl_eol":true,"virt_text":[["+","OpencodeDiffAdd"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":51,"virt_text_repeat_linebreak":false,"right_gravity":true}],[30,50,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[31,51,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[32,52,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[33,53,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[34,54,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[35,59,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:17)","OpencodeHint"],[" [msg_a0234f9c6001JCKYaca1HHwwx6]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[36,64,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 15:22:29)","OpencodeHint"],[" [msg_a0236fd1c001TlwqL8fwvq529i]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[37,65,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[38,66,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[39,69,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:22:29)","OpencodeHint"],[" [msg_a0236fd57001pTnTjSBdFlleCb]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[40,76,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:22:34)","OpencodeHint"],[" [msg_a02371241001PBQAsr8Oc9hqNI]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}],[41,80,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[42,81,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[43,82,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[44,83,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffDelete","hl_eol":true,"virt_text":[["-","OpencodeDiffDelete"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":84,"virt_text_repeat_linebreak":false,"right_gravity":true}],[45,83,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[46,84,0,{"ns_id":3,"end_col":0,"hl_group":"OpencodeDiffAdd","hl_eol":true,"virt_text":[["+","OpencodeDiffAdd"]],"end_right_gravity":false,"virt_text_hide":false,"priority":5000,"virt_text_pos":"overlay","end_row":85,"virt_text_repeat_linebreak":false,"right_gravity":true}],[47,84,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[48,85,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[49,86,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[50,87,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[51,88,0,{"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-1,"priority":4096,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"right_gravity":true}],[52,93,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:22:39)","OpencodeHint"],[" [msg_a023723d0001r87MaJThFssUw1]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"virt_text_win_col":-3,"priority":10,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"right_gravity":true}]],"timestamp":1770644135,"actions":[{"type":"diff_revert_selected_file","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"range":{"from":22,"to":22},"key":"R","text":"[R]evert file","display_line":22},{"type":"diff_revert_all","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"range":{"from":22,"to":22},"key":"A","text":"Revert [A]ll","display_line":22},{"type":"diff_open","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"range":{"from":22,"to":22},"key":"D","text":"[D]iff","display_line":22},{"type":"diff_revert_selected_file","args":["d988cc85565b99017d40ad8baea20225165be9d5"],"range":{"from":90,"to":90},"key":"R","text":"[R]evert file","display_line":90},{"type":"diff_revert_all","args":["d988cc85565b99017d40ad8baea20225165be9d5"],"range":{"from":90,"to":90},"key":"A","text":"Revert [A]ll","display_line":90},{"type":"diff_open","args":["d988cc85565b99017d40ad8baea20225165be9d5"],"range":{"from":90,"to":90},"key":"D","text":"[D]iff","display_line":90},{"type":"diff_revert_selected_file","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"range":{"from":56,"to":56},"key":"R","text":"[R]evert file","display_line":56},{"type":"diff_revert_all","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"range":{"from":56,"to":56},"key":"A","text":"Revert [A]ll","display_line":56},{"type":"diff_open","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"range":{"from":56,"to":56},"key":"D","text":"[D]iff","display_line":56}],"lines":["----","","","add another word","","[`test.txt`](test.txt)","","----","","","I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","`````txt"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"," "," ","","`````","","**󰻛 Created Snapshot** `1b6ba655`","","----","","","**Done:** added the word `again` to `test.txt`.","","----","","","add another word","","----","","","I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","","** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","----","","","Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","`````txt"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"," "," ","","`````","","**󰻛 Created Snapshot** `57d83f55`","","----","","","**Done:** appended the word `again2` to `test.txt`.","","----","","","add another word","","----","","","I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.","","** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","----","","","I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","`````txt"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3"," "," ","","`````","","**󰻛 Created Snapshot** `d988cc85`","","----","","","**Done:** appended the word `again3` to `test.txt`.","",""]} \ No newline at end of file +{ + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 15:20:02)", "OpencodeHint"], + [" [msg_a0234c0b7001y2o9S1jMaNVZar]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 4, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 5, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 6, + 8, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:04)", "OpencodeHint"], + [" [msg_a0234c7960011LTxTvD94hfWCi]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 7, + 12, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 8, + 13, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 9, + 14, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 10, + 15, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffDelete", + "hl_eol": true, + "virt_text": [["-", "OpencodeDiffDelete"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 16, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 11, + 15, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 12, + 16, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffAdd", + "hl_eol": true, + "virt_text": [["+", "OpencodeDiffAdd"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 17, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 13, + 16, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 14, + 17, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 15, + 18, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 16, + 19, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 17, + 20, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 18, + 25, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:09)", "OpencodeHint"], + [" [msg_a0234d8fb001SXyngLjuKSuxOY]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 19, + 30, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 15:20:11)", "OpencodeHint"], + [" [msg_a0234e308001SKl5bQUibp5gtI]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 20, + 31, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 21, + 32, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 22, + 35, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:11)", "OpencodeHint"], + [" [msg_a0234e31f001m4EsQdPmY3PTtS]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 23, + 42, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:16)", "OpencodeHint"], + [" [msg_a0234f482001PQbMjWc6W8s0eF]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 24, + 46, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 25, + 47, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 26, + 48, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 27, + 49, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffDelete", + "hl_eol": true, + "virt_text": [["-", "OpencodeDiffDelete"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 50, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 28, + 49, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 29, + 50, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffAdd", + "hl_eol": true, + "virt_text": [["+", "OpencodeDiffAdd"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 51, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 30, + 50, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 31, + 51, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 32, + 52, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 33, + 53, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 34, + 54, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 35, + 59, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:17)", "OpencodeHint"], + [" [msg_a0234f9c6001JCKYaca1HHwwx6]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 36, + 64, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 15:22:29)", "OpencodeHint"], + [" [msg_a0236fd1c001TlwqL8fwvq529i]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 37, + 65, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 38, + 66, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 39, + 69, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:22:29)", "OpencodeHint"], + [" [msg_a0236fd57001pTnTjSBdFlleCb]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 40, + 76, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:22:34)", "OpencodeHint"], + [" [msg_a02371241001PBQAsr8Oc9hqNI]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 41, + 80, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 42, + 81, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 43, + 82, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 44, + 83, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffDelete", + "hl_eol": true, + "virt_text": [["-", "OpencodeDiffDelete"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 84, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 45, + 83, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 46, + 84, + 0, + { + "ns_id": 3, + "end_col": 0, + "hl_group": "OpencodeDiffAdd", + "hl_eol": true, + "virt_text": [["+", "OpencodeDiffAdd"]], + "end_right_gravity": false, + "virt_text_hide": false, + "priority": 5000, + "virt_text_pos": "overlay", + "end_row": 85, + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ], + [ + 47, + 84, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 48, + 85, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 49, + 86, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 50, + 87, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 51, + 88, + 0, + { + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -1, + "priority": 4096, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "right_gravity": true + } + ], + [ + 52, + 93, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:22:39)", "OpencodeHint"], + [" [msg_a023723d0001r87MaJThFssUw1]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "virt_text_win_col": -3, + "priority": 10, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "right_gravity": true + } + ] + ], + "timestamp": 1770644135, + "actions": [ + { + "type": "diff_revert_selected_file", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "range": { "from": 22, "to": 22 }, + "key": "R", + "text": "[R]evert file", + "display_line": 22 + }, + { + "type": "diff_revert_all", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "range": { "from": 22, "to": 22 }, + "key": "A", + "text": "Revert [A]ll", + "display_line": 22 + }, + { + "type": "diff_open", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "range": { "from": 22, "to": 22 }, + "key": "D", + "text": "[D]iff", + "display_line": 22 + }, + { + "type": "diff_revert_selected_file", + "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], + "range": { "from": 90, "to": 90 }, + "key": "R", + "text": "[R]evert file", + "display_line": 90 + }, + { + "type": "diff_revert_all", + "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], + "range": { "from": 90, "to": 90 }, + "key": "A", + "text": "Revert [A]ll", + "display_line": 90 + }, + { + "type": "diff_open", + "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], + "range": { "from": 90, "to": 90 }, + "key": "D", + "text": "[D]iff", + "display_line": 90 + }, + { + "type": "diff_revert_selected_file", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "range": { "from": 56, "to": 56 }, + "key": "R", + "text": "[R]evert file", + "display_line": 56 + }, + { + "type": "diff_revert_all", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "range": { "from": 56, "to": 56 }, + "key": "A", + "text": "Revert [A]ll", + "display_line": 56 + }, + { + "type": "diff_open", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "range": { "from": 56, "to": 56 }, + "key": "D", + "text": "[D]iff", + "display_line": 56 + } + ], + "lines": [ + "----", + "", + "", + "add another word", + "", + "[`test.txt`](test.txt)", + "", + "----", + "", + "", + "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "`````txt", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + " ", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `1b6ba655`", + "", + "----", + "", + "", + "**Done:** added the word `again` to `test.txt`.", + "", + "----", + "", + "", + "add another word", + "", + "----", + "", + "", + "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "", + "** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "----", + "", + "", + "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "`````txt", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + " ", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `57d83f55`", + "", + "----", + "", + "", + "**Done:** appended the word `again2` to `test.txt`.", + "", + "----", + "", + "", + "add another word", + "", + "----", + "", + "", + "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.", + "", + "** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "----", + "", + "", + "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "`````txt", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3", + " ", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `d988cc85`", + "", + "----", + "", + "", + "**Done:** appended the word `again3` to `test.txt`.", + "", + "" + ] +} diff --git a/tests/data/redo-all.json b/tests/data/redo-all.json index 193b230b..0873780a 100644 --- a/tests/data/redo-all.json +++ b/tests/data/redo-all.json @@ -1 +1,4278 @@ -[{"properties":{},"type":"server.connected"},{"properties":{"info":{"id":"msg_a0234c0b7001y2o9S1jMaNVZar","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973602999},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0234c0b8001E8tvDdVNT0NiFh","type":"text","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"synthetic":true,"type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/test.txt\"}","id":"prt_a0234c0ba001p2wDCCzgtdcf9g","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"synthetic":true,"type":"text","text":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n00002| \n00003| \n00004| \n","id":"prt_a0234c0ba002ifWGZD21JdnYs6","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/test.txt","mime":"text/plain","id":"prt_a0234c0ba003rwMIYGQq901md0","filename":"test.txt","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973603008},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2025-10-20T15:20:00.383Z"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973605391},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234d6050012gZEes0qoo3vws","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\")","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"status":"pending"},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"},"time":{"start":1760973609159},"status":"running"},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"title":"test.txt","time":{"end":1760973609163,"start":1760973609159},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n \n \n"}},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973609181,"start":1760973609181},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"id":"prt_a0234d8df001tlclk7glXfU1WC","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"1b6ba655c6c0d899965adff278ac6320d5fc3b12","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a0234d8f0001GsHhf2mPuESk1p","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609201},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609207},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609207},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234dbf00016c9aOHUxpQxM0U","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:**","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again`","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test.txt","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test.txt`.","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973609987,"start":1760973609987},"text":"**Done:** added the word `again` to `test.txt`.","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"id":"prt_a0234dc03001iO2ubPxctfhnkY","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610000},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610001},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610002},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"id":"msg_a0234e308001SKl5bQUibp5gtI","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611784},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0234e308002nvLNQpQkMykslq","type":"text","messageID":"msg_a0234e308001SKl5bQUibp5gtI"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973611785},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234f2d9001o5Ev43oSWT3Wum","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt`","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content,","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\")","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceed","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"status":"pending"},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"time":{"start":1760973616230},"status":"running"},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973616230,"start":1760973616230},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"title":"test.txt","time":{"end":1760973616232,"start":1760973616230},"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"status":"completed","output":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n00002| \n00003| \n00004| \n","metadata":{"preview":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n\n\n"}},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"id":"prt_a0234f46800103Qi9hqHZXugdX","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616243},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616244},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616245},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234f6b0001H9XgBLZ3OdBg68","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\"","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"status":"pending"},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"},"time":{"start":1760973617573},"status":"running"},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973617574,"start":1760973617574},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"title":"test.txt","time":{"end":1760973617583,"start":1760973617573},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n \n \n"}},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"id":"prt_a0234f9af001iSzXfWH7K1FewT","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"57d83f5596cb1f142fbc681d3d93b7184f7f73cd","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a0234f9be001oL1wchagZbSt88","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617598},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617600},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617600},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234fbf1001oojqKIBCLMQ1mp","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:**","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2`","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test.txt","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test.txt`.","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973618176,"start":1760973618176},"text":"**Done:** appended the word `again2` to `test.txt`.","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"id":"prt_a0234fc00001RTCVWXgNk2myYy","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618194},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618199},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618199},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"id":"msg_a0236fd1c001TlwqL8fwvq529i","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749532},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0236fd1c002PcRJ8hOgqR7nte","type":"text","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973749535},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0237107b00170PEAkqhAQVD8H","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt`","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content,","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceed","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"status":"pending"},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"time":{"start":1760973754916},"status":"running"},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973754916,"start":1760973754916},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"title":"test.txt","time":{"end":1760973754918,"start":1760973754916},"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"status":"completed","output":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n00002| \n00003| \n00004| \n","metadata":{"preview":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n\n\n"}},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"id":"prt_a02371226001LxXIRETdzHQDxW","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754931},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754933},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754934},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a02371fcf001QAGd8I3AJYcZQW","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\"","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt`","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"status":"pending"},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3"},"time":{"start":1760973759397},"status":"running"},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973759398,"start":1760973759398},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"title":"test.txt","time":{"end":1760973759413,"start":1760973759397},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n \n \n"}},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"id":"prt_a023723b6001LJ3f2pxz1bIYRy","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"d988cc85565b99017d40ad8baea20225165be9d5","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a023723c4001SR03K7sDnOvQ21","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759429},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759430},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759431},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a02372664001tXq2uEhOCcXuDe","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:**","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3`","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test.txt","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test.txt`.","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973760125,"start":1760973760125},"text":"**Done:** appended the word `again3` to `test.txt`.","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"id":"prt_a0237267e001nad2YARrEmo6hb","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760142},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760147},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760148},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978265898},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978268997},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..387f18d 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0234e308001SKl5bQUibp5gtI"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978282075},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978577423},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"}] +[ + { "properties": {}, "type": "server.connected" }, + { + "properties": { + "info": { + "id": "msg_a0234c0b7001y2o9S1jMaNVZar", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973602999 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0234c0b8001E8tvDdVNT0NiFh", + "type": "text", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "synthetic": true, + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/test.txt\"}", + "id": "prt_a0234c0ba001p2wDCCzgtdcf9g", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "synthetic": true, + "type": "text", + "text": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n00002| \n00003| \n00004| \n", + "id": "prt_a0234c0ba002ifWGZD21JdnYs6", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/test.txt", + "mime": "text/plain", + "id": "prt_a0234c0ba003rwMIYGQq901md0", + "filename": "test.txt", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973603008 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2025-10-20T15:20:00.383Z" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973605391 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234d6050012gZEes0qoo3vws", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\")", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again" + }, + "time": { "start": 1760973609159 }, + "status": "running" + }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973609163, "start": 1760973609159 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n \n \n" + } + }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973609181, "start": 1760973609181 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "id": "prt_a0234d8df001tlclk7glXfU1WC", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "1b6ba655c6c0d899965adff278ac6320d5fc3b12", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a0234d8f0001GsHhf2mPuESk1p", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609201 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609207 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609207 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234dbf00016c9aOHUxpQxM0U", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:**", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again`", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test.txt", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test.txt`.", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973609987, "start": 1760973609987 }, + "text": "**Done:** added the word `again` to `test.txt`.", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "id": "prt_a0234dc03001iO2ubPxctfhnkY", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610000 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610001 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610002 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "id": "msg_a0234e308001SKl5bQUibp5gtI", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611784 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0234e308002nvLNQpQkMykslq", + "type": "text", + "messageID": "msg_a0234e308001SKl5bQUibp5gtI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973611785 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234f2d9001o5Ev43oSWT3Wum", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt`", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content,", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\")", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceed", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "time": { "start": 1760973616230 }, + "status": "running" + }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973616230, "start": 1760973616230 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973616232, "start": 1760973616230 }, + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "status": "completed", + "output": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n00002| \n00003| \n00004| \n", + "metadata": { + "preview": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n\n\n" + } + }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "id": "prt_a0234f46800103Qi9hqHZXugdX", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616243 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616244 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616245 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234f6b0001H9XgBLZ3OdBg68", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\"", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2" + }, + "time": { "start": 1760973617573 }, + "status": "running" + }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973617574, "start": 1760973617574 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973617583, "start": 1760973617573 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n \n \n" + } + }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "id": "prt_a0234f9af001iSzXfWH7K1FewT", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "57d83f5596cb1f142fbc681d3d93b7184f7f73cd", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a0234f9be001oL1wchagZbSt88", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617598 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617600 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617600 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234fbf1001oojqKIBCLMQ1mp", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:**", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2`", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test.txt", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test.txt`.", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973618176, "start": 1760973618176 }, + "text": "**Done:** appended the word `again2` to `test.txt`.", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a0234fc00001RTCVWXgNk2myYy", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618194 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618199 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618199 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "id": "msg_a0236fd1c001TlwqL8fwvq529i", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749532 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0236fd1c002PcRJ8hOgqR7nte", + "type": "text", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973749535 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0237107b00170PEAkqhAQVD8H", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt`", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content,", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceed", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "time": { "start": 1760973754916 }, + "status": "running" + }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973754916, "start": 1760973754916 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973754918, "start": 1760973754916 }, + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "status": "completed", + "output": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n00002| \n00003| \n00004| \n", + "metadata": { + "preview": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n\n\n" + } + }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a02371226001LxXIRETdzHQDxW", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754931 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754933 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754934 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a02371fcf001QAGd8I3AJYcZQW", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\"", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt`", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3" + }, + "time": { "start": 1760973759397 }, + "status": "running" + }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973759398, "start": 1760973759398 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973759413, "start": 1760973759397 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n \n \n" + } + }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a023723b6001LJ3f2pxz1bIYRy", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "d988cc85565b99017d40ad8baea20225165be9d5", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a023723c4001SR03K7sDnOvQ21", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759429 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759430 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759431 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a02372664001tXq2uEhOCcXuDe", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:**", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3`", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test.txt", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test.txt`.", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973760125, "start": 1760973760125 }, + "text": "**Done:** appended the word `again3` to `test.txt`.", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "id": "prt_a0237267e001nad2YARrEmo6hb", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760142 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760147 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760148 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978265898 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978268997 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..387f18d 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0234e308001SKl5bQUibp5gtI" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978282075 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978577423 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + } +] diff --git a/tests/data/redo-once.expected.json b/tests/data/redo-once.expected.json index ab2b8344..ed6d7bde 100644 --- a/tests/data/redo-once.expected.json +++ b/tests/data/redo-once.expected.json @@ -1 +1,748 @@ -{"actions":[{"text":"[R]evert file","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"display_line":56,"key":"R","range":{"to":56,"from":56},"type":"diff_revert_selected_file"},{"text":"Revert [A]ll","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"display_line":56,"key":"A","range":{"to":56,"from":56},"type":"diff_revert_all"},{"text":"[D]iff","args":["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"],"display_line":56,"key":"D","range":{"to":56,"from":56},"type":"diff_open"},{"text":"[R]evert file","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"display_line":22,"key":"R","range":{"to":22,"from":22},"type":"diff_revert_selected_file"},{"text":"Revert [A]ll","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"display_line":22,"key":"A","range":{"to":22,"from":22},"type":"diff_revert_all"},{"text":"[D]iff","args":["1b6ba655c6c0d899965adff278ac6320d5fc3b12"],"display_line":22,"key":"D","range":{"to":22,"from":22},"type":"diff_open"}],"extmarks":[[1,1,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 15:20:02)","OpencodeHint"],[" [msg_a0234c0b7001y2o9S1jMaNVZar]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[2,2,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[3,3,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[4,4,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[5,5,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[6,8,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:04)","OpencodeHint"],[" [msg_a0234c7960011LTxTvD94hfWCi]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[7,12,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[8,13,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[9,14,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[10,15,0,{"end_col":0,"end_row":16,"virt_text_pos":"overlay","virt_text_hide":false,"hl_group":"OpencodeDiffDelete","priority":5000,"ns_id":3,"hl_eol":true,"end_right_gravity":false,"virt_text":[["-","OpencodeDiffDelete"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[11,15,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[12,16,0,{"end_col":0,"end_row":17,"virt_text_pos":"overlay","virt_text_hide":false,"hl_group":"OpencodeDiffAdd","priority":5000,"ns_id":3,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[13,16,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[14,17,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[15,18,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[16,19,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[17,20,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[18,25,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:09)","OpencodeHint"],[" [msg_a0234d8fb001SXyngLjuKSuxOY]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[19,30,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-20 15:20:11)","OpencodeHint"],[" [msg_a0234e308001SKl5bQUibp5gtI]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[20,31,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[21,32,0,{"priority":4096,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[22,35,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:11)","OpencodeHint"],[" [msg_a0234e31f001m4EsQdPmY3PTtS]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[23,42,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:16)","OpencodeHint"],[" [msg_a0234f482001PQbMjWc6W8s0eF]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[24,46,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[25,47,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[26,48,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[27,49,0,{"end_col":0,"end_row":50,"virt_text_pos":"overlay","virt_text_hide":false,"hl_group":"OpencodeDiffDelete","priority":5000,"ns_id":3,"hl_eol":true,"end_right_gravity":false,"virt_text":[["-","OpencodeDiffDelete"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[28,49,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[29,50,0,{"end_col":0,"end_row":51,"virt_text_pos":"overlay","virt_text_hide":false,"hl_group":"OpencodeDiffAdd","priority":5000,"ns_id":3,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[30,50,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[31,51,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[32,52,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[33,53,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[34,54,0,{"priority":4096,"virt_text_win_col":-1,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["▌","OpencodeToolBorder"]],"right_gravity":true,"virt_text_repeat_linebreak":true}],[35,59,0,{"priority":10,"virt_text_win_col":-3,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-5-mini","OpencodeHint"],[" (2025-10-20 15:20:17)","OpencodeHint"],[" [msg_a0234f9c6001JCKYaca1HHwwx6]","OpencodeHint"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[36,69,0,{"priority":1000,"virt_text_win_col":12,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["+1","OpencodeDiffAddText"]],"right_gravity":true,"virt_text_repeat_linebreak":false}],[37,69,0,{"priority":1000,"virt_text_win_col":15,"ns_id":3,"virt_text_pos":"win_col","virt_text_hide":false,"virt_text":[["-1","OpencodeDiffDeleteText"]],"right_gravity":true,"virt_text_repeat_linebreak":false}]],"lines":["----","","","add another word","","[`test.txt`](test.txt)","","----","","","I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","`````txt"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"," "," ","","`````","","**󰻛 Created Snapshot** `1b6ba655`","","----","","","**Done:** added the word `again` to `test.txt`.","","----","","","add another word","","----","","","I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","","** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","----","","","Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","","** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`","","`````txt"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"," tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"," "," ","","`````","","**󰻛 Created Snapshot** `57d83f55`","","----","","","**Done:** appended the word `again2` to `test.txt`.","","----","","> 1 message reverted, 2 tool calls reverted",">","> type `/redo` to restore.",""," test.txt: +1 -1",""],"timestamp":1769172261} +{ + "actions": [ + { + "text": "[R]evert file", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "display_line": 56, + "key": "R", + "range": { "to": 56, "from": 56 }, + "type": "diff_revert_selected_file" + }, + { + "text": "Revert [A]ll", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "display_line": 56, + "key": "A", + "range": { "to": 56, "from": 56 }, + "type": "diff_revert_all" + }, + { + "text": "[D]iff", + "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "display_line": 56, + "key": "D", + "range": { "to": 56, "from": 56 }, + "type": "diff_open" + }, + { + "text": "[R]evert file", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "display_line": 22, + "key": "R", + "range": { "to": 22, "from": 22 }, + "type": "diff_revert_selected_file" + }, + { + "text": "Revert [A]ll", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "display_line": 22, + "key": "A", + "range": { "to": 22, "from": 22 }, + "type": "diff_revert_all" + }, + { + "text": "[D]iff", + "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "display_line": 22, + "key": "D", + "range": { "to": 22, "from": 22 }, + "type": "diff_open" + } + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 15:20:02)", "OpencodeHint"], + [" [msg_a0234c0b7001y2o9S1jMaNVZar]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 2, + 2, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 3, + 3, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 4, + 4, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 5, + 5, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 6, + 8, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:04)", "OpencodeHint"], + [" [msg_a0234c7960011LTxTvD94hfWCi]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 7, + 12, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 8, + 13, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 9, + 14, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 10, + 15, + 0, + { + "end_col": 0, + "end_row": 16, + "virt_text_pos": "overlay", + "virt_text_hide": false, + "hl_group": "OpencodeDiffDelete", + "priority": 5000, + "ns_id": 3, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["-", "OpencodeDiffDelete"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 11, + 15, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 12, + 16, + 0, + { + "end_col": 0, + "end_row": 17, + "virt_text_pos": "overlay", + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd", + "priority": 5000, + "ns_id": 3, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 13, + 16, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 14, + 17, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 15, + 18, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 16, + 19, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 17, + 20, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 18, + 25, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:09)", "OpencodeHint"], + [" [msg_a0234d8fb001SXyngLjuKSuxOY]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 19, + 30, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-20 15:20:11)", "OpencodeHint"], + [" [msg_a0234e308001SKl5bQUibp5gtI]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 20, + 31, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 21, + 32, + 0, + { + "priority": 4096, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 22, + 35, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:11)", "OpencodeHint"], + [" [msg_a0234e31f001m4EsQdPmY3PTtS]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 23, + 42, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:16)", "OpencodeHint"], + [" [msg_a0234f482001PQbMjWc6W8s0eF]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 24, + 46, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 25, + 47, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 26, + 48, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 27, + 49, + 0, + { + "end_col": 0, + "end_row": 50, + "virt_text_pos": "overlay", + "virt_text_hide": false, + "hl_group": "OpencodeDiffDelete", + "priority": 5000, + "ns_id": 3, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["-", "OpencodeDiffDelete"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 28, + 49, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 29, + 50, + 0, + { + "end_col": 0, + "end_row": 51, + "virt_text_pos": "overlay", + "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd", + "priority": 5000, + "ns_id": 3, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 30, + 50, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 31, + 51, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 32, + 52, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 33, + 53, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 34, + 54, + 0, + { + "priority": 4096, + "virt_text_win_col": -1, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeToolBorder"]], + "right_gravity": true, + "virt_text_repeat_linebreak": true + } + ], + [ + 35, + 59, + 0, + { + "priority": 10, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-5-mini", "OpencodeHint"], + [" (2025-10-20 15:20:17)", "OpencodeHint"], + [" [msg_a0234f9c6001JCKYaca1HHwwx6]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 36, + 69, + 0, + { + "priority": 1000, + "virt_text_win_col": 12, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["+1", "OpencodeDiffAddText"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ], + [ + 37, + 69, + 0, + { + "priority": 1000, + "virt_text_win_col": 15, + "ns_id": 3, + "virt_text_pos": "win_col", + "virt_text_hide": false, + "virt_text": [["-1", "OpencodeDiffDeleteText"]], + "right_gravity": true, + "virt_text_repeat_linebreak": false + } + ] + ], + "lines": [ + "----", + "", + "", + "add another word", + "", + "[`test.txt`](test.txt)", + "", + "----", + "", + "", + "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "`````txt", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + " ", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `1b6ba655`", + "", + "----", + "", + "", + "**Done:** added the word `again` to `test.txt`.", + "", + "----", + "", + "", + "add another word", + "", + "----", + "", + "", + "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "", + "** read** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "----", + "", + "", + "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/test.txt`", + "", + "`````txt", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + " ", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `57d83f55`", + "", + "----", + "", + "", + "**Done:** appended the word `again2` to `test.txt`.", + "", + "----", + "", + "> 1 message reverted, 2 tool calls reverted", + ">", + "> type `/redo` to restore.", + "", + " test.txt: +1 -1", + "" + ], + "timestamp": 1769172261 +} diff --git a/tests/data/redo-once.json b/tests/data/redo-once.json index a25520de..3abdead1 100644 --- a/tests/data/redo-once.json +++ b/tests/data/redo-once.json @@ -1 +1,4265 @@ -[{"properties":{},"type":"server.connected"},{"properties":{"info":{"id":"msg_a0234c0b7001y2o9S1jMaNVZar","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973602999},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0234c0b8001E8tvDdVNT0NiFh","type":"text","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"synthetic":true,"type":"text","text":"Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/test.txt\"}","id":"prt_a0234c0ba001p2wDCCzgtdcf9g","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"synthetic":true,"type":"text","text":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n00002| \n00003| \n00004| \n","id":"prt_a0234c0ba002ifWGZD21JdnYs6","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","type":"file","url":"file:///home/francis/Projects/_nvim/opencode.nvim/test.txt","mime":"text/plain","id":"prt_a0234c0ba003rwMIYGQq901md0","filename":"test.txt","messageID":"msg_a0234c0b7001y2o9S1jMaNVZar"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973603008},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"New session - 2025-10-20T15:20:00.383Z"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973605391},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234d6050012gZEes0qoo3vws","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\")","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973608590},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"status":"pending"},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"},"time":{"start":1760973609159},"status":"running"},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_f5xkch2vMvn01ko5Ko4GzNbL","type":"tool","state":{"title":"test.txt","time":{"end":1760973609163,"start":1760973609159},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n \n \n"}},"id":"prt_a0234d89c001EJCQkv3qyPNYjc","tool":"edit","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973609181,"start":1760973609181},"text":"I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.","id":"prt_a0234d68e0012Ej5Vj7iCasXdB","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"id":"prt_a0234d8df001tlclk7glXfU1WC","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"1b6ba655c6c0d899965adff278ac6320d5fc3b12","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a0234d8f0001GsHhf2mPuESk1p","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234c7960011LTxTvD94hfWCi"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609201},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609207},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973604758,"completed":1760973609207},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12788,"output":514,"cache":{"write":0,"read":7168}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234c7960011LTxTvD94hfWCi","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234dbf00016c9aOHUxpQxM0U","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:**","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again`","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test.txt","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973609984},"text":"**Done:** added the word `again` to `test.txt`.","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973609987,"start":1760973609987},"text":"**Done:** added the word `again` to `test.txt`.","id":"prt_a0234dc00001POFUUsQ0Iz9nze","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"id":"prt_a0234dc03001iO2ubPxctfhnkY","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234d8fb001SXyngLjuKSuxOY"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610000},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610001},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973609211,"completed":1760973610002},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12921,"output":18,"cache":{"write":0,"read":12672}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234d8fb001SXyngLjuKSuxOY","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"id":"msg_a0234e308001SKl5bQUibp5gtI","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611784},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0234e308002nvLNQpQkMykslq","type":"text","messageID":"msg_a0234e308001SKl5bQUibp5gtI"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973611785},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234f2d9001o5Ev43oSWT3Wum","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt`","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content,","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\")","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceed","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973615849},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"status":"pending"},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"time":{"start":1760973616230},"status":"running"},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973616230,"start":1760973616230},"text":"I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.","id":"prt_a0234f2e9001BHoR88k45jGihp","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_YtG0tPTwncFNNWTaUP6XeTAe","type":"tool","state":{"title":"test.txt","time":{"end":1760973616232,"start":1760973616230},"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"status":"completed","output":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n00002| \n00003| \n00004| \n","metadata":{"preview":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n\n\n"}},"id":"prt_a0234f463001u4vZX0ePvjr6ew","tool":"read","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"id":"prt_a0234f46800103Qi9hqHZXugdX","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234e31f001m4EsQdPmY3PTtS"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616243},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616244},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973611807,"completed":1760973616245},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":12949,"output":397,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234e31f001m4EsQdPmY3PTtS","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234f6b0001H9XgBLZ3OdBg68","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\"","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973616826},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"status":"pending"},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"},"time":{"start":1760973617573},"status":"running"},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973617574,"start":1760973617574},"text":"Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.","id":"prt_a0234f6ba001Rv1d0qkRn4me9r","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_a9ViHr6etx0cl2DhlyvpIsUV","type":"tool","state":{"title":"test.txt","time":{"end":1760973617583,"start":1760973617573},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n \n \n"}},"id":"prt_a0234f9910015mW83bkeVt82ej","tool":"edit","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"id":"prt_a0234f9af001iSzXfWH7K1FewT","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"57d83f5596cb1f142fbc681d3d93b7184f7f73cd","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a0234f9be001oL1wchagZbSt88","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f482001PQbMjWc6W8s0eF"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617598},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617600},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973616258,"completed":1760973617600},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13077,"output":126,"cache":{"write":0,"read":12928}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f482001PQbMjWc6W8s0eF","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0234fbf1001oojqKIBCLMQ1mp","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:**","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2`","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test.txt","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973618173},"text":"**Done:** appended the word `again2` to `test.txt`.","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973618176,"start":1760973618176},"text":"**Done:** appended the word `again2` to `test.txt`.","id":"prt_a0234fbfd001jo8HB4W6xLXRf1","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"id":"prt_a0234fc00001RTCVWXgNk2myYy","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0234f9c6001JCKYaca1HHwwx6"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618194},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618199},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973617606,"completed":1760973618199},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13212,"output":19,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0234f9c6001JCKYaca1HHwwx6","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"id":"msg_a0236fd1c001TlwqL8fwvq529i","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749532},"role":"user"}},"type":"message.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","text":"add another word\n","id":"prt_a0236fd1c002PcRJ8hOgqR7nte","type":"text","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"}},"type":"message.part.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760973749535},"directory":"/home/francis/Projects/_nvim/opencode.nvim","id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a0237107b00170PEAkqhAQVD8H","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt`","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content,","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceed","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973754529},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"status":"pending"},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"time":{"start":1760973754916},"status":"running"},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973754916,"start":1760973754916},"text":"I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.","id":"prt_a023710a1001dBBJ15P9Tda4Lz","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_4Bb3eCZ89z8Ug2FSjAxY6Xr8","type":"tool","state":{"title":"test.txt","time":{"end":1760973754918,"start":1760973754916},"input":{"filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"status":"completed","output":"\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n00002| \n00003| \n00004| \n","metadata":{"preview":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n\n\n"}},"id":"prt_a023712170010ZAVXgtaS80r0m","tool":"read","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"id":"prt_a02371226001LxXIRETdzHQDxW","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a0236fd57001pTnTjSBdFlleCb"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754931},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754933},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973749591,"completed":1760973754934},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13241,"output":457,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a0236fd57001pTnTjSBdFlleCb","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a02371fcf001QAGd8I3AJYcZQW","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\"","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt`","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973758427},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"status":"pending"},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3"},"time":{"start":1760973759397},"status":"running"},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973759398,"start":1760973759398},"text":"I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.","id":"prt_a02371fdb0014iXFfjbCQBgOam","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"file":"/home/francis/Projects/_nvim/opencode.nvim/test.txt"},"type":"file.edited"},{"properties":{"part":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","callID":"call_2GJo632y9iJGpf7eE5seXx0j","type":"tool","state":{"title":"test.txt","time":{"end":1760973759413,"start":1760973759397},"input":{"oldString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","filePath":"/home/francis/Projects/_nvim/opencode.nvim/test.txt","newString":" tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3"},"status":"completed","output":"","metadata":{"diagnostics":{},"diff":"Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n \n \n"}},"id":"prt_a02372387001SiuePn07aa1VNC","tool":"edit","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"id":"prt_a023723b6001LJ3f2pxz1bIYRy","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"hash":"d988cc85565b99017d40ad8baea20225165be9d5","type":"patch","files":["/home/francis/Projects/_nvim/opencode.nvim/test.txt"],"id":"prt_a023723c4001SR03K7sDnOvQ21","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a02371241001PBQAsr8Oc9hqNI"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759429},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759430},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973754945,"completed":1760973759431},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13367,"output":459,"cache":{"write":0,"read":13184}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a02371241001PBQAsr8Oc9hqNI","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":0,"output":0,"cache":{"write":0,"read":0}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"part":{"type":"step-start","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","id":"prt_a02372664001tXq2uEhOCcXuDe","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:**","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3`","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test.txt","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"start":1760973760122},"text":"**Done:** appended the word `again3` to `test.txt`.","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"type":"text","time":{"end":1760973760125,"start":1760973760125},"text":"**Done:** appended the word `again3` to `test.txt`.","id":"prt_a0237267a001dvydCUWE7FBrLn","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"part":{"cost":0,"type":"step-finish","tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"id":"prt_a0237267e001nad2YARrEmo6hb","sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","messageID":"msg_a023723d0001r87MaJThFssUw1"}},"type":"message.part.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760142},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760147},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"info":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","time":{"created":1760973759440,"completed":1760973760148},"mode":"build","path":{"cwd":"/home/francis/Projects/_nvim/opencode.nvim","root":"/home/francis/Projects/_nvim/opencode.nvim"},"tokens":{"reasoning":0,"input":13509,"output":19,"cache":{"write":0,"read":13312}},"cost":0,"system":["You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n","Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n"],"providerID":"github-copilot","id":"msg_a023723d0001r87MaJThFssUw1","modelID":"gpt-5-mini","role":"assistant"}},"type":"message.updated"},{"properties":{"sessionID":"ses_5fdcb4981ffe4xsF2IbnPjSWu0"},"type":"session.idle"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978265898},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978268997},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..387f18d 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0234e308001SKl5bQUibp5gtI"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"},{"properties":{"info":{"version":"0.14.6","time":{"created":1760973600383,"updated":1760978282075},"directory":"/home/francis/Projects/_nvim/opencode.nvim","revert":{"diff":"diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2","snapshot":"281b11ee830296ddff05bf14d0d7b9ef4b1ac8af","messageID":"msg_a0236fd1c001TlwqL8fwvq529i"},"id":"ses_5fdcb4981ffe4xsF2IbnPjSWu0","projectID":"29d43526f88157cd4edd071b899dd01f240b771b","title":"Adding word to test.txt"}},"type":"session.updated"}] +[ + { "properties": {}, "type": "server.connected" }, + { + "properties": { + "info": { + "id": "msg_a0234c0b7001y2o9S1jMaNVZar", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973602999 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0234c0b8001E8tvDdVNT0NiFh", + "type": "text", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "synthetic": true, + "type": "text", + "text": "Called the Read tool with the following input: {\"filePath\":\"/home/francis/Projects/_nvim/opencode.nvim/test.txt\"}", + "id": "prt_a0234c0ba001p2wDCCzgtdcf9g", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "synthetic": true, + "type": "text", + "text": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n00002| \n00003| \n00004| \n", + "id": "prt_a0234c0ba002ifWGZD21JdnYs6", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "type": "file", + "url": "file:///home/francis/Projects/_nvim/opencode.nvim/test.txt", + "mime": "text/plain", + "id": "prt_a0234c0ba003rwMIYGQq901md0", + "filename": "test.txt", + "messageID": "msg_a0234c0b7001y2o9S1jMaNVZar" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973603008 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "New session - 2025-10-20T15:20:00.383Z" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973605391 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234d6050012gZEes0qoo3vws", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\")", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973608590 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again" + }, + "time": { "start": 1760973609159 }, + "status": "running" + }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_f5xkch2vMvn01ko5Ko4GzNbL", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973609163, "start": 1760973609159 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n \n \n" + } + }, + "id": "prt_a0234d89c001EJCQkv3qyPNYjc", + "tool": "edit", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973609181, "start": 1760973609181 }, + "text": "I'll append a single word (\"again\") to the first line of `test.txt`. Applying a precise edit to the existing line now.", + "id": "prt_a0234d68e0012Ej5Vj7iCasXdB", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "id": "prt_a0234d8df001tlclk7glXfU1WC", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "1b6ba655c6c0d899965adff278ac6320d5fc3b12", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a0234d8f0001GsHhf2mPuESk1p", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234c7960011LTxTvD94hfWCi" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609201 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609207 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973604758, "completed": 1760973609207 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12788, "output": 514, "cache": { "write": 0, "read": 7168 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234c7960011LTxTvD94hfWCi", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234dbf00016c9aOHUxpQxM0U", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:**", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again`", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test.txt", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973609984 }, + "text": "**Done:** added the word `again` to `test.txt`.", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973609987, "start": 1760973609987 }, + "text": "**Done:** added the word `again` to `test.txt`.", + "id": "prt_a0234dc00001POFUUsQ0Iz9nze", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "id": "prt_a0234dc03001iO2ubPxctfhnkY", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234d8fb001SXyngLjuKSuxOY" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610000 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610001 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973609211, "completed": 1760973610002 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12921, "output": 18, "cache": { "write": 0, "read": 12672 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234d8fb001SXyngLjuKSuxOY", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "id": "msg_a0234e308001SKl5bQUibp5gtI", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611784 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0234e308002nvLNQpQkMykslq", + "type": "text", + "messageID": "msg_a0234e308001SKl5bQUibp5gtI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973611785 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234f2d9001o5Ev43oSWT3Wum", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt`", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content,", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\")", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceed", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973615849 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "time": { "start": 1760973616230 }, + "status": "running" + }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973616230, "start": 1760973616230 }, + "text": "I'll read `test.txt` to get the current first-line content, then append one word (\"again2\") to that line. Proceeding to read the file.", + "id": "prt_a0234f2e9001BHoR88k45jGihp", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_YtG0tPTwncFNNWTaUP6XeTAe", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973616232, "start": 1760973616230 }, + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "status": "completed", + "output": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n00002| \n00003| \n00004| \n", + "metadata": { + "preview": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n\n\n" + } + }, + "id": "prt_a0234f463001u4vZX0ePvjr6ew", + "tool": "read", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "id": "prt_a0234f46800103Qi9hqHZXugdX", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234e31f001m4EsQdPmY3PTtS" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616243 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616244 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973611807, "completed": 1760973616245 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 12949, "output": 397, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234e31f001m4EsQdPmY3PTtS", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234f6b0001H9XgBLZ3OdBg68", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\"", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973616826 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2" + }, + "time": { "start": 1760973617573 }, + "status": "running" + }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973617574, "start": 1760973617574 }, + "text": "Now I'll append the word \"again2\" to the first line. I'll apply an exact in-place edit to update that line.", + "id": "prt_a0234f6ba001Rv1d0qkRn4me9r", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_a9ViHr6etx0cl2DhlyvpIsUV", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973617583, "start": 1760973617573 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n \n \n" + } + }, + "id": "prt_a0234f9910015mW83bkeVt82ej", + "tool": "edit", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "id": "prt_a0234f9af001iSzXfWH7K1FewT", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "57d83f5596cb1f142fbc681d3d93b7184f7f73cd", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a0234f9be001oL1wchagZbSt88", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f482001PQbMjWc6W8s0eF" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617598 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617600 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973616258, "completed": 1760973617600 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13077, "output": 126, "cache": { "write": 0, "read": 12928 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f482001PQbMjWc6W8s0eF", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0234fbf1001oojqKIBCLMQ1mp", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:**", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2`", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test.txt", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973618173 }, + "text": "**Done:** appended the word `again2` to `test.txt`.", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973618176, "start": 1760973618176 }, + "text": "**Done:** appended the word `again2` to `test.txt`.", + "id": "prt_a0234fbfd001jo8HB4W6xLXRf1", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a0234fc00001RTCVWXgNk2myYy", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0234f9c6001JCKYaca1HHwwx6" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618194 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618199 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973617606, "completed": 1760973618199 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13212, "output": 19, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0234f9c6001JCKYaca1HHwwx6", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "id": "msg_a0236fd1c001TlwqL8fwvq529i", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749532 }, + "role": "user" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "text": "add another word\n", + "id": "prt_a0236fd1c002PcRJ8hOgqR7nte", + "type": "text", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760973749535 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a0237107b00170PEAkqhAQVD8H", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt`", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content,", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceed", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973754529 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "time": { "start": 1760973754916 }, + "status": "running" + }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973754916, "start": 1760973754916 }, + "text": "I'll read `test.txt` to get the current first-line content, then append the word `again3`. Proceeding to read the file.", + "id": "prt_a023710a1001dBBJ15P9Tda4Lz", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_4Bb3eCZ89z8Ug2FSjAxY6Xr8", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973754918, "start": 1760973754916 }, + "input": { "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, + "status": "completed", + "output": "\n00001| tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n00002| \n00003| \n00004| \n", + "metadata": { + "preview": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n\n\n" + } + }, + "id": "prt_a023712170010ZAVXgtaS80r0m", + "tool": "read", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a02371226001LxXIRETdzHQDxW", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a0236fd57001pTnTjSBdFlleCb" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754931 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754933 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973749591, "completed": 1760973754934 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13241, "output": 457, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a0236fd57001pTnTjSBdFlleCb", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a02371fcf001QAGd8I3AJYcZQW", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\"", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt`", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973758427 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { "status": "pending" }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3" + }, + "time": { "start": 1760973759397 }, + "status": "running" + }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973759398, "start": 1760973759398 }, + "text": "I'll append the word \"again3\" to the first line of `test.txt` with an exact in-place edit. Applying the change now.", + "id": "prt_a02371fdb0014iXFfjbCQBgOam", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { "properties": { "file": "/home/francis/Projects/_nvim/opencode.nvim/test.txt" }, "type": "file.edited" }, + { + "properties": { + "part": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "callID": "call_2GJo632y9iJGpf7eE5seXx0j", + "type": "tool", + "state": { + "title": "test.txt", + "time": { "end": 1760973759413, "start": 1760973759397 }, + "input": { + "oldString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "filePath": "/home/francis/Projects/_nvim/opencode.nvim/test.txt", + "newString": " tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3" + }, + "status": "completed", + "output": "", + "metadata": { + "diagnostics": {}, + "diff": "Index: /home/francis/Projects/_nvim/opencode.nvim/test.txt\n===================================================================\n--- /home/francis/Projects/_nvim/opencode.nvim/test.txt\n+++ /home/francis/Projects/_nvim/opencode.nvim/test.txt\n@@ -1,3 +1,3 @@\n-tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n \n \n" + } + }, + "id": "prt_a02372387001SiuePn07aa1VNC", + "tool": "edit", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "id": "prt_a023723b6001LJ3f2pxz1bIYRy", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "hash": "d988cc85565b99017d40ad8baea20225165be9d5", + "type": "patch", + "files": ["/home/francis/Projects/_nvim/opencode.nvim/test.txt"], + "id": "prt_a023723c4001SR03K7sDnOvQ21", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a02371241001PBQAsr8Oc9hqNI" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759429 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759430 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973754945, "completed": 1760973759431 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13367, "output": 459, "cache": { "write": 0, "read": 13184 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a02371241001PBQAsr8Oc9hqNI", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 0, "output": 0, "cache": { "write": 0, "read": 0 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "part": { + "type": "step-start", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "id": "prt_a02372664001tXq2uEhOCcXuDe", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:**", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3`", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test.txt", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "start": 1760973760122 }, + "text": "**Done:** appended the word `again3` to `test.txt`.", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "type": "text", + "time": { "end": 1760973760125, "start": 1760973760125 }, + "text": "**Done:** appended the word `again3` to `test.txt`.", + "id": "prt_a0237267a001dvydCUWE7FBrLn", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "part": { + "cost": 0, + "type": "step-finish", + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "id": "prt_a0237267e001nad2YARrEmo6hb", + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "messageID": "msg_a023723d0001r87MaJThFssUw1" + } + }, + "type": "message.part.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760142 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760147 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { + "properties": { + "info": { + "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "time": { "created": 1760973759440, "completed": 1760973760148 }, + "mode": "build", + "path": { + "cwd": "/home/francis/Projects/_nvim/opencode.nvim", + "root": "/home/francis/Projects/_nvim/opencode.nvim" + }, + "tokens": { "reasoning": 0, "input": 13509, "output": 19, "cache": { "write": 0, "read": 13312 } }, + "cost": 0, + "system": [ + "You are a coding agent running in the opencode, a terminal-based coding assistant. opencode is an open source project. You are expected to be precise, safe, and helpful.\n\nYour capabilities:\n- Receive user prompts and other context provided by the harness, such as files in the workspace.\n- Communicate with the user by streaming thinking & responses, and by making & updating plans.\n- Emit function calls to run terminal commands and apply edits. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the \"Sandbox and approvals\" section.\n\nWithin this context, Codex refers to the open-source agentic coding interface (not the old Codex language model built by OpenAI).\n\n# How you work\n\n## Personality\n\nYour default personality and tone is concise, direct, and friendly. You communicate efficiently, always keeping the user clearly informed about ongoing actions without unnecessary detail. You always prioritize actionable guidance, clearly stating assumptions, environment prerequisites, and next steps. Unless explicitly asked, you avoid excessively verbose explanations about your work.\n\n## Responsiveness\n\n### Preamble messages\n\nBefore making tool calls, send a brief preamble to the user explaining what you’re about to do. When sending preamble messages, follow these principles and examples:\n\n- **Logically group related actions**: if you’re about to run several related commands, describe them together in one preamble rather than sending a separate note for each.\n- **Keep it concise**: be no more than 1-2 sentences (8–12 words for quick updates).\n- **Build on prior context**: if this is not your first tool call, use the preamble message to connect the dots with what’s been done so far and create a sense of momentum and clarity for the user to understand your next actions.\n- **Keep your tone light, friendly and curious**: add small touches of personality in preambles feel collaborative and engaging.\n\n**Examples:**\n- “I’ve explored the repo; now checking the API route definitions.”\n- “Next, I’ll edit the config and update the related tests.”\n- “I’m about to scaffold the CLI commands and helper functions.”\n- “Ok cool, so I’ve wrapped my head around the repo. Now digging into the API routes.”\n- “Config’s looking tidy. Next up is editing helpers to keep things in sync.”\n- “Finished poking at the DB gateway. I will now chase down error handling.”\n- “Alright, build pipeline order is interesting. Checking how it reports failures.”\n- “Spotted a clever caching util; now hunting where it gets used.”\n\n**Avoiding a preamble for every trivial read (e.g., `cat` a single file) unless it’s part of a larger grouped action.\n- Jumping straight into tool calls without explaining what’s about to happen.\n- Writing overly long or speculative preambles — focus on immediate, tangible next steps.\n\n## Planning\n\nYou have access to an `todowrite` tool which tracks steps and progress and\nrenders them to the user. Using the tool helps demonstrate that you've\nunderstood the task and convey how you're approaching it. Plans can help to make\ncomplex, ambiguous, or multi-phase work clearer and more collaborative for the\nuser. A good plan should break the task into meaningful, logically ordered steps\nthat are easy to verify as you go. Note that plans are not for padding out\nsimple work with filler steps or stating the obvious. Do not repeat the full\ncontents of the plan after a `todowrite` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.\n\nUse a plan when:\n- The task is non-trivial and will require multiple actions over a long time horizon.\n- There are logical phases or dependencies where sequencing matters.\n- The work has ambiguity that benefits from outlining high-level goals.\n- You want intermediate checkpoints for feedback and validation.\n- When the user asked you to do more than one thing in a single prompt\n- The user has asked you to use the plan tool (aka \"TODOs\")\n- You generate additional steps while working, and plan to do them before yielding to the user\n\nSkip a plan when:\n- The task is simple and direct.\n- Breaking it down would only produce literal or trivial steps.\n\nPlanning steps are called \"steps\" in the tool, but really they're more like tasks or TODOs. As such they should be very concise descriptions of non-obvious work that an engineer might do like \"Write the API spec\", then \"Update the backend\", then \"Implement the frontend\". On the other hand, it's obvious that you'll usually have to \"Explore the codebase\" or \"Implement the changes\", so those are not worth tracking in your plan.\n\nIt may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.\n\n### Examples\n\n**High-quality plans**\n\nExample 1:\n\n1. Add CLI entry with file args\n2. Parse Markdown via CommonMark library\n3. Apply semantic HTML template\n4. Handle code blocks, images, links\n5. Add error handling for invalid files\n\nExample 2:\n\n1. Define CSS variables for colors\n2. Add toggle with localStorage state\n3. Refactor components to use variables\n4. Verify all views for readability\n5. Add smooth theme-change transition\n\nExample 3:\n\n1. Set up Node.js + WebSocket server\n2. Add join/leave broadcast events\n3. Implement messaging with timestamps\n4. Add usernames + mention highlighting\n5. Persist messages in lightweight DB\n6. Add typing indicators + unread count\n\n**Low-quality plans**\n\nExample 1:\n\n1. Create CLI tool\n2. Add Markdown parser\n3. Convert to HTML\n\nExample 2:\n\n1. Add dark mode toggle\n2. Save preference\n3. Make styles look good\n\nExample 3:\n\n1. Create single-file HTML game\n2. Run quick sanity check\n3. Summarize usage instructions\n\nIf you need to write a plan, only write high quality plans, not low quality ones.\n\n## Task execution\n\nYou are a coding agent. Please keep going until the query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved. Autonomously resolve the query to the best of your ability, using the tools available to you, before coming back to the user. Do NOT guess or make up an answer.\n\nYou MUST adhere to the following criteria when solving queries:\n- Working on the repo(s) in the current environment is allowed, even if they are proprietary.\n- Analyzing code for vulnerabilities is allowed.\n- Showing user code and tool call details is allowed.\n- Use the `edit` tool to edit files \n\nIf completing the user's task requires writing or modifying files, your code and final answer should follow these coding guidelines, though user instructions (i.e. AGENTS.md) may override these guidelines:\n\n- Fix the problem at the root cause rather than applying surface-level edits, when possible.\n- Avoid unneeded complexity in your solution.\n- Do not attempt to fix unrelated bugs or broken tests. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n- Update documentation as necessary.\n- Keep changes consistent with the style of the existing codebase. Changes should be minimal and focused on the task.\n- Use `git log` and `git blame` to search the history of the codebase if additional context is required.\n- NEVER add copyright or license headers unless specifically requested.\n- Do not waste tokens by re-reading files after calling `edit` on them. The tool call will fail if it didn't work. The same goes for making folders, deleting folders, etc.\n- Do not `git commit` your changes or create new git branches unless explicitly requested.\n- Do not add inline comments within code unless explicitly requested.\n- Do not use one-letter variable names unless explicitly requested.\n- NEVER output inline citations like \"【F:README.md†L5-L14】\" in your outputs. The CLI is not able to render these so they will just be broken in the UI. Instead, if you output valid filepaths, users will be able to click on them to open the files in their editor.\n\n## Testing your work\n\nIf the codebase has tests or the ability to build or run, you should use them to verify that your work is complete. Generally, your testing philosophy should be to start as specific as possible to the code you changed so that you can catch issues efficiently, then make your way to broader tests as you build confidence. If there's no test for the code you changed, and if the adjacent patterns in the codebases show that there's a logical place for you to add a test, you may do so. However, do not add tests to codebases with no tests, or where the patterns don't indicate so.\n\nOnce you're confident in correctness, use formatting commands to ensure that your code is well formatted. These commands can take time so you should run them on as precise a target as possible. If there are issues you can iterate up to 3 times to get formatting right, but if you still can't manage it's better to save the user time and present them a correct solution where you call out the formatting in your final message. If the codebase does not have a formatter configured, do not add one.\n\nFor all of testing, running, building, and formatting, do not attempt to fix unrelated bugs. It is not your responsibility to fix them. (You may mention them to the user in your final message though.)\n\n## Sandbox and approvals\n\nThe Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.\n\nFilesystem sandboxing prevents you from editing files without user approval. The options are:\n- *read-only*: You can only read files.\n- *workspace-write*: You can read files. You can write to files in your workspace folder, but not outside it.\n- *danger-full-access*: No filesystem sandboxing.\n\nNetwork sandboxing prevents you from accessing network without approval. Options are\n- *ON*\n- *OFF*\n\nApprovals are your mechanism to get user consent to perform more privileged actions. Although they introduce friction to the user because your work is paused until the user responds, you should leverage them to accomplish your important work. Do not let these settings or the sandbox deter you from attempting to accomplish the user's task. Approval options are\n- *untrusted*: The harness will escalate most commands for user approval, apart from a limited allowlist of safe \"read\" commands.\n- *on-failure*: The harness will allow all commands to run in the sandbox (if enabled), and failures will be escalated to the user for approval to run again without the sandbox.\n- *on-request*: Commands will be run in the sandbox by default, and you can specify in your tool call if you want to escalate a command to run without sandboxing. (Note that this mode is not always available. If it is, you'll see parameters for it in the `shell` command description.)\n- *never*: This is a non-interactive mode where you may NEVER ask the user for approval to run commands. Instead, you must always persist and work around constraints to solve the task for the user. You MUST do your utmost best to finish the task and validate your work before yielding. If this mode is pared with `danger-full-access`, take advantage of it to deliver the best outcome for the user. Further, in this mode, your default testing philosophy is overridden: Even if you don't see local patterns for testing, you may add tests and scripts to validate your work. Just remove them before yielding.\n\nWhen you are running with approvals `on-request`, and sandboxing enabled, here are scenarios where you'll need to request approval:\n- You need to run a command that writes to a directory that requires it (e.g. running tests that write to /tmp)\n- You need to run a GUI app (e.g., open/xdg-open/osascript) to open browsers or files.\n- You are running sandboxed and need to run a command that requires network access (e.g. installing packages)\n- If you run a command that is important to solving the user's query, but it fails because of sandboxing, rerun the command with approval.\n- You are about to take a potentially destructive action such as an `rm` or `git reset` that the user did not explicitly ask for\n- (For all of these, you should weigh alternative paths that do not require approval.)\n\nNote that when sandboxing is set to read-only, you'll need to request approval for any command that isn't a read.\n\nYou will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing ON, and approval on-failure.\n\n## Ambition vs. precision\n\nFor tasks that have no prior context (i.e. the user is starting something brand new), you should feel free to be ambitious and demonstrate creativity with your implementation.\n\nIf you're operating in an existing codebase, you should make sure you do exactly what the user asks with surgical precision. Treat the surrounding codebase with respect, and don't overstep (i.e. changing filenames or variables unnecessarily). You should balance being sufficiently ambitious and proactive when completing tasks of this nature.\n\nYou should use judicious initiative to decide on the right level of detail and complexity to deliver based on the user's needs. This means showing good judgment that you're capable of doing the right extras without gold-plating. This might be demonstrated by high-value, creative touches when scope of the task is vague; while being surgical and targeted when scope is tightly specified.\n\n## Sharing progress updates\n\nFor especially longer tasks that you work on (i.e. requiring many tool calls, or a plan with multiple steps), you should provide progress updates back to the user at reasonable intervals. These updates should be structured as a concise sentence or two (no more than 8-10 words long) recapping progress so far in plain language: this update demonstrates your understanding of what needs to be done, progress so far (i.e. files explores, subtasks complete), and where you're going next.\n\nBefore doing large chunks of work that may incur latency as experienced by the user (i.e. writing a new file), you should send a concise message to the user with an update indicating what you're about to do to ensure they know what you're spending time on. Don't start editing or writing large files before informing the user what you are doing and why.\n\nThe messages you send before tool calls should describe what is immediately about to be done next in very concise language. If there was previous work done, this preamble message should also include a note about the work done so far to bring the user along.\n\n## Presenting your work and final message\n\nYour final message should read naturally, like an update from a concise teammate. For casual conversation, brainstorming tasks, or quick questions from the user, respond in a friendly, conversational tone. You should ask questions, suggest ideas, and adapt to the user’s style. If you've finished a large amount of work, when describing what you've done to the user, you should follow the final answer formatting guidelines to communicate substantive changes. You don't need to add structured formatting for one-word answers, greetings, or purely conversational exchanges.\n\nYou can skip heavy formatting for single, simple actions or confirmations. In these cases, respond in plain sentences with any relevant next step or quick option. Reserve multi-section structured responses for results that need grouping or explanation.\n\nThe user is working on the same computer as you, and has access to your work. As\nsuch there's no need to show the full contents of large files you have already\nwritten unless the user explicitly asks for them. Similarly, if you've created\nor modified files using `edit`, there's no need to tell users to \"save the file\" or \"copy the code into a file\"—just reference the file path.\n\nIf there's something that you think you could help with as a logical next step, concisely ask the user if they want you to do so. Good examples of this are running tests, committing changes, or building out the next logical component. If there’s something that you couldn't do (even with approval) but that the user might want to do (such as verifying changes by running the app), include those instructions succinctly.\n\nBrevity is very important as a default. You should be very concise (i.e. no more than 10 lines), but can relax this requirement for tasks where additional detail and comprehensiveness is important for the user's understanding.\n\n### Final answer structure and style guidelines\n\nYou are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.\n\n**Section Headers**\n- Use only when they improve clarity — they are not mandatory for every answer.\n- Choose descriptive names that fit the content\n- Keep headers short (1–3 words) and in `**Title Case**`. Always start headers with `**` and end with `**`\n- Leave no blank line before the first bullet under a header.\n- Section headers should only be used where they genuinely improve scanability; avoid fragmenting the answer.\n\n**Bullets**\n- Use `-` followed by a space for every bullet.\n- Bold the keyword, then colon + concise description.\n- Merge related points when possible; avoid a bullet for every trivial detail.\n- Keep bullets to one line unless breaking for clarity is unavoidable.\n- Group into short lists (4–6 bullets) ordered by importance.\n- Use consistent keyword phrasing and formatting across sections.\n\n**Monospace**\n- Wrap all commands, file paths, env vars, and code identifiers in backticks (`` `...` ``).\n- Apply to inline examples and to bullet keywords if the keyword itself is a literal file/command.\n- Never mix monospace and bold markers; choose one based on whether it’s a keyword (`**`) or inline code/path (`` ` ``).\n\n**Structure**\n- Place related bullets together; don’t mix unrelated concepts in the same section.\n- Order sections from general → specific → supporting info.\n- For subsections (e.g., “Binaries” under “Rust Workspace”), introduce with a bolded keyword bullet, then list items under it.\n- Match structure to complexity:\n - Multi-part or detailed results → use clear headers and grouped bullets.\n - Simple results → minimal headers, possibly just a short list or paragraph.\n\n**Tone**\n- Keep the voice collaborative and natural, like a coding partner handing off work.\n- Be concise and factual — no filler or conversational commentary and avoid unnecessary repetition\n- Use present tense and active voice (e.g., “Runs tests” not “This will run tests”).\n- Keep descriptions self-contained; don’t refer to “above” or “below”.\n- Use parallel structure in lists for consistency.\n\n**Don’t**\n- Don’t use literal words “bold” or “monospace” in the content.\n- Don’t nest bullets or create deep hierarchies.\n- Don’t output ANSI escape codes directly — the CLI renderer applies them.\n- Don’t cram unrelated keywords into a single bullet; split for clarity.\n- Don’t let keyword lists run long — wrap or reformat for scanability.\n\nGenerally, ensure your final answers adapt their shape and depth to the request. For example, answers to code explanations should have a precise, structured explanation with code references that answer the question directly. For tasks with a simple implementation, lead with the outcome and supplement only with what’s needed for clarity. Larger changes can be presented as a logical walkthrough of your approach, grouping related steps, explaining rationale where it adds value, and highlighting next actions to accelerate the user. Your answers should provide the right level of detail while being easily scannable.\n\nFor casual greetings, acknowledgements, or other one-off conversational messages that are not delivering substantive information or structured results, respond naturally without section headers or bullet formatting.\n", + "Here is some useful information about the environment you are running in:\n\n Working directory: /home/francis/Projects/_nvim/opencode.nvim\n Is directory a git repo: yes\n Platform: linux\n Today's date: Mon Oct 20 2025\n\n\n .github/\n\tworkflows/\n\t\ttests.yml\nlua/\n\topencode/\n\t\tui/\n\t\t\tcompletion/\n\t\t\t\tengines/\n\t\t\t\t\tblink_cmp.lua\n\t\t\t\t\tnvim_cmp.lua\n\t\t\t\t\tvim_complete.lua\n\t\t\t\tcommands.lua\n\t\t\t\tfiles.lua\n\t\t\t\tsort.lua\n\t\t\t\tsubagents.lua\n\t\t\tautocmds.lua\n\t\t\tcompletion.lua\n\t\t\tcontextual_actions.lua\n\t\t\tdebug_helper.lua\n\t\t\tdiff_tab.lua\n\t\t\tfile_picker.lua\n\t\t\tfooter.lua\n\t\t\tformatter.lua\n\t\t\thighlight.lua\n\t\t\ticons.lua\n\t\t\tinput_window.lua\n\t\t\tloading_animation.lua\n\t\t\tmention.lua\n\t\t\tnavigation.lua\n\t\t\toutput_window.lua\n\t\t\toutput.lua\n\t\t\tpicker.lua\n\t\t\trender_state.lua\n\t\t\trenderer.lua\n\t\t\tsession_picker.lua\n\t\t\ttimer.lua\n\t\t\ttopbar.lua\n\t\t\tui.lua\n\t\tapi_client.lua\n\t\tapi.lua\n\t\tconfig_file.lua\n\t\tconfig.lua\n\t\tcontext.lua\n\t\tcore.lua\n\t\tcurl.lua\n\t\tevent_manager.lua\n\t\tgit_review.lua\n\t\thealth.lua\n\t\thistory.lua\n\t\tid.lua\n\t\tinit.lua\n\t\tkeymap.lua\n\t\topencode_server.lua\n\t\tpromise.lua\n\t\tprovider.lua\n\t\treview.lua\n\t\tserver_job.lua\n\t\tsession.lua\n\t\tsnapshot.lua\n\t\tstate.lua\n\t\ttypes.lua\n\t\tutil.lua\nplugin/\n\topencode.lua\ntests/\n\tdata/\n\t\tdiff.expected.json\n\t\tdiff.json\n\t\tmessage-removal.expected.json\n\t\tmessage-removal.json\n\t\tpermission-denied.expected.json\n\t\tpermission-denied.json\n\t\tpermission-prompt.expected.json\n\t\tpermission-prompt.json\n\t\tpermission.expected.json\n\t\tpermission.json\n\t\tplanning.expected.json\n\t\tplanning.json\n\t\trevert.expected.json\n\t\trevert.json\n\t\tselection.expected.json\n\t\tselection.json\n\t\tshifting-and-multiple-perms.expected.json\n\t\tshifting-and-multiple-perms.json\n\t\tsimple-session.expected.json\n\t\tsimple-session.json\n\t\ttool-invalid.expected.json\n\t\ttool-invalid.json\n\t\tupdating-text.expected.json\n\t\tupdating-text.json\n\tmanual/\n\t\tinit_replay.lua\n\t\tQUICKSTART.md\n\t\tREADME.md\n\t\tregenerate_expected.sh\n\t\trenderer_replay.lua\n\t\trun_replay.sh\n\tminimal/\n\t\tinit.lua\n\t\tplugin_spec.lua\n\tmocks/\n\t\tsession_list.lua\n\tunit/\n\t\tapi_client_spec.lua\n\t\tapi_spec.lua\n\t\tconfig_file_spec.lua\n\t\tconfig_spec.lua\n\t\tcontext_spec.lua\n\t\tcore_spec.lua\n\t\tevent_manager_spec.lua\n\t\tid_spec.lua\n\t\tinit_spec.lua\n\t\tkeymap_spec.lua\n\t\topencode_server_spec.lua\n\t\trender_state_spec.lua\n\t\trenderer_spec.lua\n\t\tserver_job_spec.lua\n\t\tsession_spec.lua\n\t\tsnapshot_spec.lua\n\t\tstate_spec.lua\n\t\ttimer_spec.lua\n\t\tutil_spec.lua\n\thelpers.lua\n.emmyrc.json\n.gitignore\n.stylua.toml\nLICENSE\nREADME.md\nrun_tests.sh\ntest.txt\n\n# AGENTS.md\n\n## Build, Lint, and Test\n\n- **Run all tests:** `./run_tests.sh`\n- **Minimal tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/minimal', {minimal_init = './tests/minimal/init.lua', sequential = true})\"`\n- **Unit tests:**\n `nvim --headless -u tests/minimal/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Run a single test:** Replace the directory in the above command with the test file path, e.g.:\n `nvim --headless -u tests/manual/init.lua -c \"lua require('plenary.test_harness').test_directory('./tests/unit/job_spec.lua', {minimal_init = './tests/minimal/init.lua'})\"`\n- **Manual/Visual tests:** `./tests/manual/run_replay.sh` - Replay captured event data for visual testing\n- **Debug rendering in headless mode:**\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/FILE.json\" \"+ReplayAll 1\" \"+sleep 500m | qa!\"`\n This will replay events and dump the output buffer to stdout, useful for debugging rendering issues without a UI.\n You can run to just a specific message # with (e.g. message # 12):\n `nvim --headless -u tests/manual/init_replay.lua \"+ReplayHeadless\" \"+ReplayLoad tests/data/message-removal.json\" \"+ReplayNext 12\" \"+sleep 500m | qa\"`\n ```\n\n ```\n- **Lint:** No explicit lint command; follow Lua best practices.\n\n## Code Style Guidelines\n\n- **Imports:** `local mod = require('mod')` at the top. Group standard, then project imports.\n- **Formatting:** 2 spaces per indent. No trailing whitespace. Lines ≤ 100 chars.\n- **Types:** Use Lua annotations (`---@class`, `---@field`, etc.) for public APIs/config.\n- **Naming:** Modules: `snake_case.lua`; functions/vars: `snake_case`; classes: `CamelCase`.\n- **Error Handling:** Use `vim.notify` for user-facing errors. Return early on error.\n- **Comments:** Only when necessary for clarity. Prefer self-explanatory code.\n- **Functions:** Prefer local functions. Use `M.func` for module exports.\n- **Config:** Centralize in `config.lua`. Use deep merge for user overrides.\n- **Tests:** Place in `tests/minimal/` or `tests/unit/`. Manual/visual tests in `tests/manual/`.\n\n_Agentic coding agents must follow these conventions strictly for consistency and reliability._\n" + ], + "providerID": "github-copilot", + "id": "msg_a023723d0001r87MaJThFssUw1", + "modelID": "gpt-5-mini", + "role": "assistant" + } + }, + "type": "message.updated" + }, + { "properties": { "sessionID": "ses_5fdcb4981ffe4xsF2IbnPjSWu0" }, "type": "session.idle" }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978265898 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978268997 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..387f18d 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0234e308001SKl5bQUibp5gtI" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + }, + { + "properties": { + "info": { + "version": "0.14.6", + "time": { "created": 1760973600383, "updated": 1760978282075 }, + "directory": "/home/francis/Projects/_nvim/opencode.nvim", + "revert": { + "diff": "diff --git a/test.txt b/test.txt\nindex 7191e44..f2caa07 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,3 +1,3 @@\n- tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2 again3\n+ tangram quiver saffron nebula cobalt murmur plinth zephyr ember lattice cadenza another yet extra more again again2", + "snapshot": "281b11ee830296ddff05bf14d0d7b9ef4b1ac8af", + "messageID": "msg_a0236fd1c001TlwqL8fwvq529i" + }, + "id": "ses_5fdcb4981ffe4xsF2IbnPjSWu0", + "projectID": "29d43526f88157cd4edd071b899dd01f240b771b", + "title": "Adding word to test.txt" + } + }, + "type": "session.updated" + } +] diff --git a/tests/data/revert.expected.json b/tests/data/revert.expected.json index cd1b917f..5c0415e9 100644 --- a/tests/data/revert.expected.json +++ b/tests/data/revert.expected.json @@ -1 +1,541 @@ -{"lines":["----","","","write 10 random words","","[`poem.md`](poem.md)","","----","","","Here are 10 random words:","","1. Lantern ","2. Whisper ","3. Velvet ","4. Orbit ","5. Timber ","6. Quiver ","7. Mosaic ","8. Ember ","9. Spiral ","10. Glimmer","","Let me know if you need them in a specific format or want to use them in a file!","","----","","","write 10 random words to the file","","----","","","I will write 10 random words to poem.md, each on a new line.","","Proceeding to update the file now.","","** write** `/home/francis/Projects/_nvim/opencode.nvim/poem.md`","","`````markdown","Lantern","Whisper","Velvet","Orbit","Timber","Quiver","Mosaic","Ember","Spiral","Glimmer","","`````","","**󰻛 Created Snapshot** `c410b2b4`","","----","","","The file poem.md has been updated with 10 random words, each on a new line. Task complete! If you need anything else, let me know.","","----","","> 2 messages reverted, 4 tool calls reverted",">","> type `/redo` to restore.",""," poem.md: -20",""],"actions":[{"text":"[R]evert file","display_line":53,"key":"R","range":{"to":53,"from":53},"args":["c410b2b4024de020aea223c5248eec89216de53f"],"type":"diff_revert_selected_file"},{"text":"Revert [A]ll","display_line":53,"key":"A","range":{"to":53,"from":53},"args":["c410b2b4024de020aea223c5248eec89216de53f"],"type":"diff_revert_all"},{"text":"[D]iff","display_line":53,"key":"D","range":{"to":53,"from":53},"args":["c410b2b4024de020aea223c5248eec89216de53f"],"type":"diff_open"}],"extmarks":[[1,1,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-19 17:50:43)","OpencodeHint"],[" [msg_9fd985573001fk1Xlot7uyDgTo]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[2,2,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[3,3,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[4,4,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[5,5,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[6,8,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-19 17:50:44)","OpencodeHint"],[" [msg_9fd985a4d001wOX3Op7CpFiCTq]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[7,26,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-19 17:50:57)","OpencodeHint"],[" [msg_9fd988c92001w0IZCVPQsN6xa9]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[8,27,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[9,28,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[10,31,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-19 17:50:57)","OpencodeHint"],[" [msg_9fd988ca7001lgaGttpI4YeGSA]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[11,37,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[12,38,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[13,39,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[14,40,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[15,41,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[16,42,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[17,43,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[18,44,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[19,45,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[20,46,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[21,47,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[22,48,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[23,49,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[24,50,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[25,51,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"virt_text_pos":"win_col","virt_text_win_col":-1,"priority":4096,"right_gravity":true}],[26,56,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" gpt-4.1","OpencodeHint"],[" (2025-10-19 17:50:59)","OpencodeHint"],[" [msg_9fd98942d001elqd2sd8CZeOoA]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[27,66,0,{"ns_id":3,"virt_text_hide":false,"virt_text_repeat_linebreak":false,"virt_text":[["-20","OpencodeDiffDeleteText"]],"virt_text_pos":"win_col","virt_text_win_col":11,"priority":1000,"right_gravity":true}]],"timestamp":1769172286} +{ + "lines": [ + "----", + "", + "", + "write 10 random words", + "", + "[`poem.md`](poem.md)", + "", + "----", + "", + "", + "Here are 10 random words:", + "", + "1. Lantern ", + "2. Whisper ", + "3. Velvet ", + "4. Orbit ", + "5. Timber ", + "6. Quiver ", + "7. Mosaic ", + "8. Ember ", + "9. Spiral ", + "10. Glimmer", + "", + "Let me know if you need them in a specific format or want to use them in a file!", + "", + "----", + "", + "", + "write 10 random words to the file", + "", + "----", + "", + "", + "I will write 10 random words to poem.md, each on a new line.", + "", + "Proceeding to update the file now.", + "", + "** write** `/home/francis/Projects/_nvim/opencode.nvim/poem.md`", + "", + "`````markdown", + "Lantern", + "Whisper", + "Velvet", + "Orbit", + "Timber", + "Quiver", + "Mosaic", + "Ember", + "Spiral", + "Glimmer", + "", + "`````", + "", + "**󰻛 Created Snapshot** `c410b2b4`", + "", + "----", + "", + "", + "The file poem.md has been updated with 10 random words, each on a new line. Task complete! If you need anything else, let me know.", + "", + "----", + "", + "> 2 messages reverted, 4 tool calls reverted", + ">", + "> type `/redo` to restore.", + "", + " poem.md: -20", + "" + ], + "actions": [ + { + "text": "[R]evert file", + "display_line": 53, + "key": "R", + "range": { "to": 53, "from": 53 }, + "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "type": "diff_revert_selected_file" + }, + { + "text": "Revert [A]ll", + "display_line": 53, + "key": "A", + "range": { "to": 53, "from": 53 }, + "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "type": "diff_revert_all" + }, + { + "text": "[D]iff", + "display_line": 53, + "key": "D", + "range": { "to": 53, "from": 53 }, + "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "type": "diff_open" + } + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-19 17:50:43)", "OpencodeHint"], + [" [msg_9fd985573001fk1Xlot7uyDgTo]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 4, + 4, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 5, + 5, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 6, + 8, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-10-19 17:50:44)", "OpencodeHint"], + [" [msg_9fd985a4d001wOX3Op7CpFiCTq]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 7, + 26, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-19 17:50:57)", "OpencodeHint"], + [" [msg_9fd988c92001w0IZCVPQsN6xa9]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 8, + 27, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 9, + 28, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 10, + 31, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-10-19 17:50:57)", "OpencodeHint"], + [" [msg_9fd988ca7001lgaGttpI4YeGSA]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 11, + 37, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 12, + 38, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 13, + 39, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 14, + 40, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 15, + 41, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 16, + 42, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 17, + 43, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 18, + 44, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 19, + 45, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 20, + 46, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 21, + 47, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 22, + 48, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 23, + 49, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 24, + 50, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 25, + 51, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -1, + "priority": 4096, + "right_gravity": true + } + ], + [ + 26, + 56, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" gpt-4.1", "OpencodeHint"], + [" (2025-10-19 17:50:59)", "OpencodeHint"], + [" [msg_9fd98942d001elqd2sd8CZeOoA]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 27, + 66, + 0, + { + "ns_id": 3, + "virt_text_hide": false, + "virt_text_repeat_linebreak": false, + "virt_text": [["-20", "OpencodeDiffDeleteText"]], + "virt_text_pos": "win_col", + "virt_text_win_col": 11, + "priority": 1000, + "right_gravity": true + } + ] + ], + "timestamp": 1769172286 +} diff --git a/tests/data/selection.expected.json b/tests/data/selection.expected.json index e950370c..f6d0e006 100644 --- a/tests/data/selection.expected.json +++ b/tests/data/selection.expected.json @@ -1 +1,289 @@ -{"timestamp":1766432128,"actions":[],"lines":["----","","","here's some context","","```txt","this is a string","```","","```txt","this is a selection test","```","","[`diff-test.txt`](diff-test.txt)","","----","","","I can see the file `/Users/cam/tmp/a/diff-test.txt` contains \"this is a string\" on line 1.","","The two selection contexts you provided show:","1. Current content: \"this is a string\"","2. Expected/desired content: \"this is a selection test\"","","Since I'm in read-only mode, I can only observe that there's a difference between what's currently in the file and what one of the selections indicates should be there.","",""],"extmarks":[[1,1,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-18 05:02:28)","OpencodeHint"],[" [msg_9f5b29fea001z6jYXF7CG9omHa]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}],[2,2,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[3,3,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[4,4,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[5,5,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[6,6,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[7,7,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[8,8,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[9,8,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[10,9,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[11,10,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[12,11,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[13,12,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[14,12,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[15,13,0,{"virt_text_repeat_linebreak":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":4096,"right_gravity":true}],[16,16,0,{"virt_text_repeat_linebreak":false,"virt_text_hide":false,"ns_id":3,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-18 05:02:28)","OpencodeHint"],[" [msg_9f5b2a039001xop8ITmXQq0Gjh]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_win_col":-3,"priority":10,"right_gravity":true}]]} +{ + "timestamp": 1766432128, + "actions": [], + "lines": [ + "----", + "", + "", + "here's some context", + "", + "```txt", + "this is a string", + "```", + "", + "```txt", + "this is a selection test", + "```", + "", + "[`diff-test.txt`](diff-test.txt)", + "", + "----", + "", + "", + "I can see the file `/Users/cam/tmp/a/diff-test.txt` contains \"this is a string\" on line 1.", + "", + "The two selection contexts you provided show:", + "1. Current content: \"this is a string\"", + "2. Expected/desired content: \"this is a selection test\"", + "", + "Since I'm in read-only mode, I can only observe that there's a difference between what's currently in the file and what one of the selections indicates should be there.", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-18 05:02:28)", "OpencodeHint"], + [" [msg_9f5b29fea001z6jYXF7CG9omHa]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ], + [ + 2, + 2, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 3, + 3, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 4, + 4, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 5, + 5, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 6, + 6, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 7, + 7, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 8, + 8, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 9, + 8, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 10, + 9, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 11, + 10, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 12, + 11, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 13, + 12, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 14, + 12, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 15, + 13, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true + } + ], + [ + 16, + 16, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-18 05:02:28)", "OpencodeHint"], + [" [msg_9f5b2a039001xop8ITmXQq0Gjh]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_win_col": -3, + "priority": 10, + "right_gravity": true + } + ] + ] +} diff --git a/tests/data/shifting-and-multiple-perms.expected.json b/tests/data/shifting-and-multiple-perms.expected.json index 686b2f64..6ab1fefc 100644 --- a/tests/data/shifting-and-multiple-perms.expected.json +++ b/tests/data/shifting-and-multiple-perms.expected.json @@ -1 +1,798 @@ -{"actions":[],"extmarks":[[1,1,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-17 01:05:49)","OpencodeHint"],[" [msg_9efb39d68001J2h30a50B2774b]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[2,2,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[3,3,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[4,4,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[5,5,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[6,8,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-17 01:05:50)","OpencodeHint"],[" [msg_9efb39dc3002f81rMRqF2WO1UU]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[7,83,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-17 01:07:23)","OpencodeHint"],[" [msg_9efb50a0b001WFK7AMDV45cF8Z]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[8,84,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[9,85,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[10,88,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-17 01:07:23)","OpencodeHint"],[" [msg_9efb50a2a002dzMgbQnasd86o1]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[11,111,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-17 01:08:01)","OpencodeHint"],[" [msg_9efb59d93001LSm9y0DS9p8cP6]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[12,112,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[13,113,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodeMessageRoleUser"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[14,116,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-17 01:08:01)","OpencodeHint"],[" [msg_9efb59db4002uWmyFRTjRIhIaQ]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[15,125,0,{"right_gravity":true,"virt_text_win_col":-3,"ns_id":3,"virt_text_hide":false,"virt_text":[[" ","OpencodeMessageRoleSystem"],[" "],["SYSTEM","OpencodeMessageRoleSystem"],["","OpencodeHint"],["","OpencodeHint"],[" [permission-display-message]","OpencodeHint"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10}],[16,127,0,{"right_gravity":true,"ns_id":3,"line_hl_group":"OpencodePermissionTitle","priority":4096}],[17,127,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[18,128,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[19,129,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[20,130,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[21,131,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[22,132,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[23,133,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[24,134,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[25,135,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[26,136,0,{"end_col":0,"hl_group":"OpencodeDiffAdd","right_gravity":true,"ns_id":3,"virt_text_hide":false,"end_row":137,"hl_eol":true,"end_right_gravity":false,"virt_text":[["+","OpencodeDiffAdd"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":5000}],[27,136,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[28,137,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[29,138,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[30,139,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[31,140,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[32,141,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[33,142,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[34,143,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[35,144,0,{"right_gravity":true,"ns_id":3,"line_hl_group":"OpencodeDialogOptionHover","priority":4096}],[36,144,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[37,144,2,{"right_gravity":true,"virt_text_hide":false,"ns_id":3,"virt_text":[["› ","OpencodeDialogOptionHover"]],"virt_text_pos":"overlay","virt_text_repeat_linebreak":false,"priority":4096}],[38,145,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[39,146,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[40,147,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}],[41,148,0,{"right_gravity":true,"virt_text_win_col":-2,"ns_id":3,"virt_text_hide":false,"virt_text":[["▌","OpencodePermissionBorder"]],"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096}]],"timestamp":1769449853,"lines":["----","","","no, i want the extra line added when i've streamed the most recent part but i don't want it still there when i add the next part. i.e. i want an extra blank line at the end","","[`lua/opencode/ui/renderer.lua`](lua/opencode/ui/renderer.lua)","","----","","","Ah, I understand now! You want:","1. An extra blank line at the very end of the buffer when content has been streamed","2. But when **new** content is about to be added, remove that extra blank line first (so the new content doesn't have an awkward gap above it)","","This is a \"trailing spacer\" that should only exist when we're \"done\" with the current output, but gets removed before adding more.","","Here's the approach:","","## Solution","","Add tracking for whether a trailing blank line exists, and:","- **Add** a blank line after writing new content (in `_write_formatted_data`)","- **Remove** the trailing blank line before writing new content (also in `_write_formatted_data`)","","### Changes needed in `renderer.lua`:","","1. **Add state tracking** (after line 14):"," ```lua"," M._has_trailing_line = false"," ```","","2. **Reset the flag** in `M.reset()` (after line 21):"," ```lua"," M._has_trailing_line = false"," ```","","3. **Modify `_write_formatted_data`** (around line 247-276):"," - Before writing: remove trailing line if it exists"," - After writing: add a new trailing line"," "," ```lua"," function M._write_formatted_data(formatted_data)"," local buf = state.windows.output_buf"," local start_line = output_window.get_buf_line_count()"," "," -- Remove trailing blank line before adding new content"," if M._has_trailing_line and start_line > 0 then"," output_window.set_lines({}, start_line - 1, start_line)"," start_line = start_line - 1"," M._has_trailing_line = false"," end"," "," local new_lines = formatted_data.lines"," local extmarks = formatted_data.extmarks"," "," if #new_lines == 0 or not buf then"," return nil"," end"," "," -- [existing action extraction code...]"," "," output_window.set_lines(new_lines, start_line)"," output_window.set_extmarks(extmarks, start_line)"," "," -- Add trailing blank line after writing"," local end_line = start_line + #new_lines"," output_window.set_lines({''}, end_line)"," M._has_trailing_line = true"," "," return {"," line_start = start_line,"," line_end = start_line + #new_lines - 1,"," }"," end"," ```","","This way:","- ✅ Extra blank line appears after streaming content","- ✅ Gets removed automatically before next content is added","- ✅ No gap between messages","- ✅ Always has visual spacing at the bottom","","----","","","but i'd only want to do that for things added to the end","","----","","","Ah yes, you're right! The trailing blank line should only be added/removed when writing to the **end** of the buffer, not when doing replacements in the middle (like `_replace_part_in_buffer`).","","Let me trace through the code:","","1. **`_write_formatted_data`** - writes at end (line 247-276)","2. **`_insert_part_to_buffer`** - calls `_write_formatted_data`, so writes at end (line 282-300)","3. **`_replace_part_in_buffer`** - replaces in middle of buffer (line 307-353)","","So the logic should be:","- In **`_write_formatted_data`**: Remove trailing line before writing (if exists), then add it back after","- In **`_replace_part_in_buffer`**: Don't touch the trailing line at all","- In **`render_output`**: This sets the entire buffer, so no trailing line management needed","","The key insight is that `_write_formatted_data` is specifically for appending to the end (line 249: `start_line = output_window.get_buf_line_count()`), so that's the only place where we need to:","1. Remove the trailing blank line first (if it exists)","2. Write the new content","3. Add a new trailing blank line","","Does that match what you want? The trailing line only exists at the very end, and only gets managed when appending new content to the end.","","----","","","yes","","----","","","** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`","","** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`","","** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`","","----","",""," Permission Required (1/3)",""," *edit* `Edit this file: /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`","","`````"," M._part_cache = {}"," M._prev_line_count = 0"," M._message_map = MessageMap.new()"," M._actions = {}"," M._has_trailing_line = false"," "," ---Reset renderer state"," function M.reset()"," M._part_cache = {}","","`````",""," 1. Allow once "," 2. Reject"," 3. Allow always","","Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`","",""]} +{ + "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-17 01:05:49)", "OpencodeHint"], + [" [msg_9efb39d68001J2h30a50B2774b]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 2, + 2, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 3, + 3, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 4, + 4, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 5, + 5, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 6, + 8, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-17 01:05:50)", "OpencodeHint"], + [" [msg_9efb39dc3002f81rMRqF2WO1UU]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 7, + 83, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-17 01:07:23)", "OpencodeHint"], + [" [msg_9efb50a0b001WFK7AMDV45cF8Z]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 8, + 84, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 9, + 85, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 10, + 88, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-17 01:07:23)", "OpencodeHint"], + [" [msg_9efb50a2a002dzMgbQnasd86o1]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 11, + 111, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-17 01:08:01)", "OpencodeHint"], + [" [msg_9efb59d93001LSm9y0DS9p8cP6]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 12, + 112, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 13, + 113, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 14, + 116, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-17 01:08:01)", "OpencodeHint"], + [" [msg_9efb59db4002uWmyFRTjRIhIaQ]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [ + 15, + 125, + 0, + { + "right_gravity": true, + "virt_text_win_col": -3, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [ + [" ", "OpencodeMessageRoleSystem"], + [" "], + ["SYSTEM", "OpencodeMessageRoleSystem"], + ["", "OpencodeHint"], + ["", "OpencodeHint"], + [" [permission-display-message]", "OpencodeHint"] + ], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10 + } + ], + [16, 127, 0, { "right_gravity": true, "ns_id": 3, "line_hl_group": "OpencodePermissionTitle", "priority": 4096 }], + [ + 17, + 127, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 18, + 128, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 19, + 129, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 20, + 130, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 21, + 131, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 22, + 132, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 23, + 133, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 24, + 134, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 25, + 135, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 26, + 136, + 0, + { + "end_col": 0, + "hl_group": "OpencodeDiffAdd", + "right_gravity": true, + "ns_id": 3, + "virt_text_hide": false, + "end_row": 137, + "hl_eol": true, + "end_right_gravity": false, + "virt_text": [["+", "OpencodeDiffAdd"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 5000 + } + ], + [ + 27, + 136, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 28, + 137, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 29, + 138, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 30, + 139, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 31, + 140, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 32, + 141, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 33, + 142, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 34, + 143, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [35, 144, 0, { "right_gravity": true, "ns_id": 3, "line_hl_group": "OpencodeDialogOptionHover", "priority": 4096 }], + [ + 36, + 144, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 37, + 144, + 2, + { + "right_gravity": true, + "virt_text_hide": false, + "ns_id": 3, + "virt_text": [["› ", "OpencodeDialogOptionHover"]], + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false, + "priority": 4096 + } + ], + [ + 38, + 145, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 39, + 146, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 40, + 147, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ], + [ + 41, + 148, + 0, + { + "right_gravity": true, + "virt_text_win_col": -2, + "ns_id": 3, + "virt_text_hide": false, + "virt_text": [["▌", "OpencodePermissionBorder"]], + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096 + } + ] + ], + "timestamp": 1769449853, + "lines": [ + "----", + "", + "", + "no, i want the extra line added when i've streamed the most recent part but i don't want it still there when i add the next part. i.e. i want an extra blank line at the end", + "", + "[`lua/opencode/ui/renderer.lua`](lua/opencode/ui/renderer.lua)", + "", + "----", + "", + "", + "Ah, I understand now! You want:", + "1. An extra blank line at the very end of the buffer when content has been streamed", + "2. But when **new** content is about to be added, remove that extra blank line first (so the new content doesn't have an awkward gap above it)", + "", + "This is a \"trailing spacer\" that should only exist when we're \"done\" with the current output, but gets removed before adding more.", + "", + "Here's the approach:", + "", + "## Solution", + "", + "Add tracking for whether a trailing blank line exists, and:", + "- **Add** a blank line after writing new content (in `_write_formatted_data`)", + "- **Remove** the trailing blank line before writing new content (also in `_write_formatted_data`)", + "", + "### Changes needed in `renderer.lua`:", + "", + "1. **Add state tracking** (after line 14):", + " ```lua", + " M._has_trailing_line = false", + " ```", + "", + "2. **Reset the flag** in `M.reset()` (after line 21):", + " ```lua", + " M._has_trailing_line = false", + " ```", + "", + "3. **Modify `_write_formatted_data`** (around line 247-276):", + " - Before writing: remove trailing line if it exists", + " - After writing: add a new trailing line", + " ", + " ```lua", + " function M._write_formatted_data(formatted_data)", + " local buf = state.windows.output_buf", + " local start_line = output_window.get_buf_line_count()", + " ", + " -- Remove trailing blank line before adding new content", + " if M._has_trailing_line and start_line > 0 then", + " output_window.set_lines({}, start_line - 1, start_line)", + " start_line = start_line - 1", + " M._has_trailing_line = false", + " end", + " ", + " local new_lines = formatted_data.lines", + " local extmarks = formatted_data.extmarks", + " ", + " if #new_lines == 0 or not buf then", + " return nil", + " end", + " ", + " -- [existing action extraction code...]", + " ", + " output_window.set_lines(new_lines, start_line)", + " output_window.set_extmarks(extmarks, start_line)", + " ", + " -- Add trailing blank line after writing", + " local end_line = start_line + #new_lines", + " output_window.set_lines({''}, end_line)", + " M._has_trailing_line = true", + " ", + " return {", + " line_start = start_line,", + " line_end = start_line + #new_lines - 1,", + " }", + " end", + " ```", + "", + "This way:", + "- ✅ Extra blank line appears after streaming content", + "- ✅ Gets removed automatically before next content is added", + "- ✅ No gap between messages", + "- ✅ Always has visual spacing at the bottom", + "", + "----", + "", + "", + "but i'd only want to do that for things added to the end", + "", + "----", + "", + "", + "Ah yes, you're right! The trailing blank line should only be added/removed when writing to the **end** of the buffer, not when doing replacements in the middle (like `_replace_part_in_buffer`).", + "", + "Let me trace through the code:", + "", + "1. **`_write_formatted_data`** - writes at end (line 247-276)", + "2. **`_insert_part_to_buffer`** - calls `_write_formatted_data`, so writes at end (line 282-300)", + "3. **`_replace_part_in_buffer`** - replaces in middle of buffer (line 307-353)", + "", + "So the logic should be:", + "- In **`_write_formatted_data`**: Remove trailing line before writing (if exists), then add it back after", + "- In **`_replace_part_in_buffer`**: Don't touch the trailing line at all", + "- In **`render_output`**: This sets the entire buffer, so no trailing line management needed", + "", + "The key insight is that `_write_formatted_data` is specifically for appending to the end (line 249: `start_line = output_window.get_buf_line_count()`), so that's the only place where we need to:", + "1. Remove the trailing blank line first (if it exists)", + "2. Write the new content", + "3. Add a new trailing blank line", + "", + "Does that match what you want? The trailing line only exists at the very end, and only gets managed when appending new content to the end.", + "", + "----", + "", + "", + "yes", + "", + "----", + "", + "", + "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`", + "", + "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`", + "", + "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`", + "", + "----", + "", + "", + " Permission Required (1/3)", + "", + " *edit* `Edit this file: /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/renderer.lua`", + "", + "`````", + " M._part_cache = {}", + " M._prev_line_count = 0", + " M._message_map = MessageMap.new()", + " M._actions = {}", + " M._has_trailing_line = false", + " ", + " ---Reset renderer state", + " function M.reset()", + " M._part_cache = {}", + "", + "`````", + "", + " 1. Allow once ", + " 2. Reject", + " 3. Allow always", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", + "", + "" + ] +} diff --git a/tests/data/simple-session.expected.json b/tests/data/simple-session.expected.json index ed346f39..ddec7561 100644 --- a/tests/data/simple-session.expected.json +++ b/tests/data/simple-session.expected.json @@ -1 +1,157 @@ -{"actions":[],"timestamp":1766432128,"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-10 19:18:25)","OpencodeHint"],[" [msg_9cf8f64de0016tbfTQqWMydbdr]","OpencodeHint"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[4,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[5,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[6,6,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[7,7,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":true,"priority":4096,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}],[8,10,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-10 19:18:25)","OpencodeHint"],[" [msg_9cf8f6549001tpoRuqkwS4Rxtl]","OpencodeHint"]],"right_gravity":true,"virt_text_pos":"win_col","virt_text_repeat_linebreak":false,"priority":10,"virt_text_hide":false,"virt_text_win_col":-3,"ns_id":3}]],"lines":["----","","","only answer the following, nothing else:","","1","","[`a-empty.txt`](a-empty.txt)","","----","","","1","",""]} +{ + "actions": [], + "timestamp": 1766432128, + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-10 19:18:25)", "OpencodeHint"], + [" [msg_9cf8f64de0016tbfTQqWMydbdr]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 4, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 5, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 6, + 6, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 7, + 7, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "priority": 4096, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ], + [ + 8, + 10, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-10 19:18:25)", "OpencodeHint"], + [" [msg_9cf8f6549001tpoRuqkwS4Rxtl]", "OpencodeHint"] + ], + "right_gravity": true, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "priority": 10, + "virt_text_hide": false, + "virt_text_win_col": -3, + "ns_id": 3 + } + ] + ], + "lines": [ + "----", + "", + "", + "only answer the following, nothing else:", + "", + "1", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "1", + "", + "" + ] +} diff --git a/tests/data/tool-invalid.expected.json b/tests/data/tool-invalid.expected.json index 92886a00..66e8fff5 100644 --- a/tests/data/tool-invalid.expected.json +++ b/tests/data/tool-invalid.expected.json @@ -1 +1,131 @@ -{"timestamp":1766432128,"actions":[],"lines":["----","","","** tool** `invalid`","","> [!ERROR]",">","> Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.","> Error message: JSON Parse error: Unterminated string","",""],"extmarks":[[1,1,0,{"virt_text_repeat_linebreak":false,"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["BUILD","OpencodeMessageRoleAssistant"],[" claude-sonnet-4.5","OpencodeHint"],[" (2025-10-13 20:10:06)","OpencodeHint"],[" [msg_9df31cc90001HGn2UbFUgqJnLr]","OpencodeHint"]],"ns_id":3,"virt_text_hide":false,"priority":10,"right_gravity":true,"virt_text_win_col":-3,"virt_text_pos":"win_col"}],[2,3,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}],[3,4,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}],[4,5,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}],[5,6,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}],[6,7,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}],[7,8,0,{"virt_text_repeat_linebreak":true,"virt_text":[["▌","OpencodeToolBorder"]],"ns_id":3,"virt_text_hide":false,"priority":4096,"right_gravity":true,"virt_text_win_col":-1,"virt_text_pos":"win_col"}]]} \ No newline at end of file +{ + "timestamp": 1766432128, + "actions": [], + "lines": [ + "----", + "", + "", + "** tool** `invalid`", + "", + "> [!ERROR]", + ">", + "> Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.", + "> Error message: JSON Parse error: Unterminated string", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text_repeat_linebreak": false, + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["BUILD", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4.5", "OpencodeHint"], + [" (2025-10-13 20:10:06)", "OpencodeHint"], + [" [msg_9df31cc90001HGn2UbFUgqJnLr]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_hide": false, + "priority": 10, + "right_gravity": true, + "virt_text_win_col": -3, + "virt_text_pos": "win_col" + } + ], + [ + 2, + 3, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ], + [ + 3, + 4, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ], + [ + 4, + 5, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ], + [ + 5, + 6, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ], + [ + 6, + 7, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ], + [ + 7, + 8, + 0, + { + "virt_text_repeat_linebreak": true, + "virt_text": [["▌", "OpencodeToolBorder"]], + "ns_id": 3, + "virt_text_hide": false, + "priority": 4096, + "right_gravity": true, + "virt_text_win_col": -1, + "virt_text_pos": "win_col" + } + ] + ] +} diff --git a/tests/data/updating-text.expected.json b/tests/data/updating-text.expected.json index 3ce892ea..9941c802 100644 --- a/tests/data/updating-text.expected.json +++ b/tests/data/updating-text.expected.json @@ -1 +1,174 @@ -{"timestamp":1769091027,"actions":[],"lines":["----","","","What would a new neovim lua plugin look like?","","[`a-empty.txt`](a-empty.txt)","","----","","","A new Neovim Lua plugin typically follows this structure:","","```","plugin-name/","├── lua/","│ └── plugin-name/","│ ├── init.lua -- Main entry point","│ ├── config.lua -- Configuration handling","│ └── utils.lua -- Utility functions","├── plugin/","│ └── plugin-name.lua -- Plugin registration","└──  README.md","```","","**Minimal example:**","","`plugin/example.lua`:","```lua","if vim.g.loaded_example then"," return","end","vim.g.loaded_example = 1","","vim.api.nvim_create_user_command('Example', function()"," require('example').hello()","end, {})","```","","`lua/example/init.lua`:","```lua","local M = {}","","M.setup = function(opts)"," opts = opts or {}"," -- Handle configuration","end","","M.hello = function()"," print(\"Hello from my plugin!\")","end","","return M","```","","Key components:","- Use `vim.api` for Neovim API calls","- Provide a `setup()` function for configuration","- Create user commands with `nvim_create_user_command`","- Use autocommands with `nvim_create_autocmd`","- Follow Lua module patterns with `local M = {}`","",""],"extmarks":[[1,1,0,{"virt_text":[["▌󰭻 ","OpencodeMessageRoleUser"],[" "],["USER","OpencodeMessageRoleUser"],["","OpencodeHint"],[" (2025-10-10 22:06:43)","OpencodeHint"],[" [msg_9d0297a630014CA5ly3Vvw8Kt5]","OpencodeHint"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_repeat_linebreak":false,"virt_text_hide":false}],[2,2,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_repeat_linebreak":true,"virt_text_hide":false}],[3,3,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_repeat_linebreak":true,"virt_text_hide":false}],[4,4,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_repeat_linebreak":true,"virt_text_hide":false}],[5,5,0,{"virt_text":[["▌","OpencodeMessageRoleUser"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":4096,"virt_text_win_col":-3,"virt_text_repeat_linebreak":true,"virt_text_hide":false}],[6,8,0,{"virt_text":[[" ","OpencodeMessageRoleAssistant"],[" "],["PLAN","OpencodeMessageRoleAssistant"],[" claude-sonnet-4","OpencodeHint"],[" (2025-10-10 22:06:43)","OpencodeHint"],[" [msg_9d0297ab3001UGZU9fDJM4Y75w]","OpencodeHint"]],"ns_id":3,"virt_text_pos":"win_col","right_gravity":true,"priority":10,"virt_text_win_col":-3,"virt_text_repeat_linebreak":false,"virt_text_hide":false}]]} +{ + "timestamp": 1769091027, + "actions": [], + "lines": [ + "----", + "", + "", + "What would a new neovim lua plugin look like?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "A new Neovim Lua plugin typically follows this structure:", + "", + "```", + "plugin-name/", + "├── lua/", + "│ └── plugin-name/", + "│ ├── init.lua -- Main entry point", + "│ ├── config.lua -- Configuration handling", + "│ └── utils.lua -- Utility functions", + "├── plugin/", + "│ └── plugin-name.lua -- Plugin registration", + "└──  README.md", + "```", + "", + "**Minimal example:**", + "", + "`plugin/example.lua`:", + "```lua", + "if vim.g.loaded_example then", + " return", + "end", + "vim.g.loaded_example = 1", + "", + "vim.api.nvim_create_user_command('Example', function()", + " require('example').hello()", + "end, {})", + "```", + "", + "`lua/example/init.lua`:", + "```lua", + "local M = {}", + "", + "M.setup = function(opts)", + " opts = opts or {}", + " -- Handle configuration", + "end", + "", + "M.hello = function()", + " print(\"Hello from my plugin!\")", + "end", + "", + "return M", + "```", + "", + "Key components:", + "- Use `vim.api` for Neovim API calls", + "- Provide a `setup()` function for configuration", + "- Create user commands with `nvim_create_user_command`", + "- Use autocommands with `nvim_create_autocmd`", + "- Follow Lua module patterns with `local M = {}`", + "", + "" + ], + "extmarks": [ + [ + 1, + 1, + 0, + { + "virt_text": [ + ["▌󰭻 ", "OpencodeMessageRoleUser"], + [" "], + ["USER", "OpencodeMessageRoleUser"], + ["", "OpencodeHint"], + [" (2025-10-10 22:06:43)", "OpencodeHint"], + [" [msg_9d0297a630014CA5ly3Vvw8Kt5]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false, + "virt_text_hide": false + } + ], + [ + 2, + 2, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true, + "virt_text_hide": false + } + ], + [ + 3, + 3, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true, + "virt_text_hide": false + } + ], + [ + 4, + 4, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true, + "virt_text_hide": false + } + ], + [ + 5, + 5, + 0, + { + "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 4096, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": true, + "virt_text_hide": false + } + ], + [ + 6, + 8, + 0, + { + "virt_text": [ + [" ", "OpencodeMessageRoleAssistant"], + [" "], + ["PLAN", "OpencodeMessageRoleAssistant"], + [" claude-sonnet-4", "OpencodeHint"], + [" (2025-10-10 22:06:43)", "OpencodeHint"], + [" [msg_9d0297ab3001UGZU9fDJM4Y75w]", "OpencodeHint"] + ], + "ns_id": 3, + "virt_text_pos": "win_col", + "right_gravity": true, + "priority": 10, + "virt_text_win_col": -3, + "virt_text_repeat_linebreak": false, + "virt_text_hide": false + } + ] + ] +} From 341c69bbe56b7a9e807cd2f950ced70ea4cf2a61 Mon Sep 17 00:00:00 2001 From: disrupted Date: Tue, 10 Feb 2026 19:34:44 +0100 Subject: [PATCH 2/3] refactor: format JSON & sort keys for `:ReplaySave` --- tests/manual/renderer_replay.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/renderer_replay.lua b/tests/manual/renderer_replay.lua index 28ef9060..0f85af32 100644 --- a/tests/manual/renderer_replay.lua +++ b/tests/manual/renderer_replay.lua @@ -201,7 +201,7 @@ function M.save_output(filename) snapshot = M.normalize_file_paths(snapshot) if filename then - local json = vim.json.encode(snapshot) + local json = vim.json.encode(snapshot, { indent = ' ', sort_keys = true }) local f = io.open(filename, 'w') if not f then From 1225a829a5ea426c539ed5561b5623c9a2ae50f9 Mon Sep 17 00:00:00 2001 From: disrupted Date: Tue, 10 Feb 2026 19:37:06 +0100 Subject: [PATCH 3/3] style: regenerate replay snapshots --- tests/data/ansi-codes.expected.json | 6944 ++++++++----- tests/data/api-abort.expected.json | 138 +- tests/data/api-error.expected.json | 241 +- tests/data/cursor_data.expected.json | 286 +- tests/data/diagnostics.expected.json | 9248 +++++++++++------ tests/data/diff.expected.json | 397 +- tests/data/markdown-codefence.expected.json | 748 +- tests/data/mentions-with-ranges.expected.json | 405 +- tests/data/message-removal.expected.json | 243 +- .../multiple-messages-synthetic.expected.json | 440 +- tests/data/multiple-messages.expected.json | 468 +- ...tiple-question-ask-reply-all.expected.json | 485 +- .../data/multiple-question-ask.expected.json | 424 +- tests/data/perf.expected.json | 156 +- .../permission-ask-new-approve.expected.json | 546 +- .../permission-ask-new-deny.expected.json | 236 +- tests/data/permission-ask-new.expected.json | 490 +- tests/data/permission-prompt.expected.json | 338 +- tests/data/permission.expected.json | 267 +- tests/data/planning.expected.json | 337 +- tests/data/question-ask-replied.expected.json | 276 +- tests/data/question-ask.expected.json | 328 +- tests/data/reasoning.expected.json | 305 +- tests/data/redo-all.expected.json | 1187 ++- tests/data/redo-once.expected.json | 867 +- tests/data/revert.expected.json | 702 +- tests/data/selection.expected.json | 380 +- .../shifting-and-multiple-perms.expected.json | 711 +- tests/data/simple-session.expected.json | 174 +- tests/data/tool-invalid.expected.json | 163 +- tests/data/updating-text.expected.json | 270 +- 31 files changed, 18510 insertions(+), 9690 deletions(-) diff --git a/tests/data/ansi-codes.expected.json b/tests/data/ansi-codes.expected.json index b0d97def..672d3171 100644 --- a/tests/data/ansi-codes.expected.json +++ b/tests/data/ansi-codes.expected.json @@ -1,437 +1,43 @@ { - "lines": [ - "----", - "", - "", - "** run** `Run all tests to verify refactoring`", - "", - "`````bash", - "> cd /Users/cam/Dev/neovim-dev/opencode.nvim && ./run_tests.sh", - "", - "Running tests for opencode.nvim", - "------------------------------------------------", - "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/minimal/plugin_spec.lua\t", - "Success\t||\topencode.nvim plugin loads the plugin without errors\t", - "Success\t||\topencode.nvim plugin can be set up with custom config\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "✓ Minimal tests passed", - "------------------------------------------------", - "Starting...Scheduling: ./tests/unit/timer_spec.lua", - "Scheduling: ./tests/unit/server_job_spec.lua", - "Scheduling: ./tests/unit/config_spec.lua", - "Scheduling: ./tests/unit/api_spec.lua", - "Scheduling: ./tests/unit/event_manager_spec.lua", - "Scheduling: ./tests/unit/init_spec.lua", - "Scheduling: ./tests/unit/state_spec.lua", - "Scheduling: ./tests/unit/id_spec.lua", - "Scheduling: ./tests/unit/api_client_spec.lua", - "Scheduling: ./tests/unit/context_spec.lua", - "Scheduling: ./tests/unit/session_spec.lua", - "Scheduling: ./tests/unit/config_file_spec.lua", - "Scheduling: ./tests/unit/renderer_spec.lua", - "Scheduling: ./tests/unit/opencode_server_spec.lua", - "Scheduling: ./tests/unit/core_spec.lua", - "Scheduling: ./tests/unit/render_state_spec.lua", - "Scheduling: ./tests/unit/snapshot_spec.lua", - "Scheduling: ./tests/unit/keymap_spec.lua", - "Scheduling: ./tests/unit/util_spec.lua", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/server_job_spec.lua\t", - "Success\t||\tserver_job exposes expected public functions\t", - "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", - "Success\t||\tserver_job call_api rejects on non 2xx\t", - "Success\t||\tserver_job stream_api forwards chunks\t", - "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/util_spec.lua\t", - "Success\t||\tutil.parse_dot_args parses flat booleans\t", - "Success\t||\tutil.parse_dot_args parses nested dot notation\t", - "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", - "Success\t||\tutil.parse_dot_args parses numbers\t", - "Success\t||\tutil.parse_dot_args handles empty string\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_client_spec.lua\t", - "Success\t||\tapi_client should create a new client instance\t", - "Success\t||\tapi_client should remove trailing slash from base_url\t", - "Success\t||\tapi_client should create client using create factory function\t", - "Success\t||\tapi_client should have all expected API methods\t", - "Success\t||\tapi_client should construct URLs correctly with query parameters\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/session_spec.lua\t", - "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", - "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", - "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", - "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", - "Success\t||\topencode.session get_by_name returns the session with matching ID\t", - "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", - "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", - "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", - "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", - "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", - "Success\t||\topencode.session get_messages returns nil when session is nil\t", - "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", - "Success\t||\topencode.session get_messages returns messages with their parts\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_file_spec.lua\t", - "Success\t||\tconfig_file.setup lazily loads config when accessed\t", - "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", - "Success\t||\tconfig_file.setup get_opencode_project returns project\t", - "\t", - "Success: \t3\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_spec.lua\t", - "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", - "Success\t||\topencode.api setup registers all commands\t", - "Success\t||\topencode.api setup sets up command functions that call the correct core functions\t", - "Success\t||\topencode.api Lua API provides callable functions that match commands\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/id_spec.lua\t", - "Success\t||\tID module should generate ascending session IDs\t", - "Success\t||\tID module should generate descending message IDs\t", - "Success\t||\tID module should validate given IDs correctly\t", - "Success\t||\tID module should throw error for invalid given IDs\t", - "Success\t||\tID module should validate schemas correctly\t", - "Success\t||\tID module should return available prefixes\t", - "Success\t||\tID module should generate IDs with correct length structure\t", - "\t", - "Success: \t7\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/render_state_spec.lua\t", - "Success\t||\tRenderState new and reset creates a new instance\t", - "Success\t||\tRenderState new and reset resets to empty state\t", - "Success\t||\tRenderState set_message sets a new message\t", - "Success\t||\tRenderState set_message updates line index for message\t", - "Success\t||\tRenderState set_message updates existing message\t", - "Success\t||\tRenderState set_part sets a new part\t", - "Success\t||\tRenderState set_part updates line index for part\t", - "Success\t||\tRenderState set_part initializes actions array\t", - "Success\t||\tRenderState get_part_at_line returns part at line\t", - "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", - "Success\t||\tRenderState get_message_at_line returns message at line\t", - "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", - "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", - "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", - "Success\t||\tRenderState actions adds actions to part\t", - "Success\t||\tRenderState actions adds actions with offset\t", - "Success\t||\tRenderState actions clears actions for part\t", - "Success\t||\tRenderState actions gets actions at line\t", - "Success\t||\tRenderState actions gets all actions from all parts\t", - "Success\t||\tRenderState update_part_lines updates part line positions\t", - "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", - "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", - "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", - "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", - "Success\t||\tRenderState remove_part clears line index for removed part\t", - "Success\t||\tRenderState remove_part returns false for non-existent part\t", - "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", - "Success\t||\tRenderState remove_message clears line index for removed message\t", - "Success\t||\tRenderState remove_message returns false for non-existent message\t", - "Success\t||\tRenderState remove_message removes unrendered message without shifting\t", - "Success\t||\tRenderState shift_all does nothing when delta is 0\t", - "Success\t||\tRenderState shift_all shifts content at or after from_line\t", - "Success\t||\tRenderState shift_all shifts actions with parts\t", - "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", - "Success\t||\tRenderState shift_all invalidates index when content shifted\t", - "Success\t||\tRenderState shift_all exits early when content found before from_line\t", - "Success\t||\tRenderState update_part_data updates part reference\t", - "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", - "Success\t||\tRenderState get_unrendered_message_ids returns empty list when no unrendered messages\t", - "Success\t||\tRenderState get_unrendered_message_ids returns list of unrendered message IDs\t", - "Success\t||\tRenderState get_unrendered_message_ids returns sorted list of message IDs\t", - "\t", - "Success: \t41\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/init_spec.lua\t", - "Success\t||\topencode has setup function in the public API\t", - "Success\t||\topencode main module can be required without errors\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/snapshot_spec.lua\t", - "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", - "Success\t||\tsnapshot.restore notifies error if no active session\t", - "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", - "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/event_manager_spec.lua\t", - "Success\t||\tEventManager should create a new instance\t", - "Success\t||\tEventManager should subscribe and emit events\t", - "Success\t||\tEventManager should handle multiple subscribers\t", - "Success\t||\tEventManager should unsubscribe correctly\t", - "Success\t||\tEventManager should track subscriber count\t", - "Success\t||\tEventManager should list event names\t", - "Success\t||\tEventManager should handle starting and stopping\t", - "Success\t||\tEventManager should not start multiple times\t", - "\t", - "Success: \t8\t", - "Failed : \t0\t", - "========================================\t", - "Error detected while processing command line:", - "opencode command not found - please install and configure opencode before using this plugin", - "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/core_spec.lua\t", - "Success\t||\topencode.core open creates windows if they don't exist\t", - "Success\t||\topencode.core open handles new session properly\t", - "Success\t||\topencode.core open focuses the appropriate window\t", - "Success\t||\topencode.core select_session filters sessions by description and parentID\t", - "Success\t||\topencode.core send_message sends a message via api_client\t", - "Success\t||\topencode.core send_message creates new session when none active\t", - "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", - "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", - "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", - "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", - "\t", - "Success: \t10\t", - "Failed : \t0\t", - "========================================\t", - "File not added to context. Could not read.", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/context_spec.lua\t", - "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", - "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", - "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", - "Success\t||\textract_legacy_tag extracts content between tags\t", - "Success\t||\textract_legacy_tag returns nil if tag not found\t", - "Success\t||\tformat_message returns a parts array with prompt as first part\t", - "Success\t||\tformat_message includes mentioned_files and subagents\t", - "Success\t||\tdelta_context removes current_file if unchanged\t", - "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", - "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/opencode_server_spec.lua\t", - "Success\t||\topencode.opencode_server creates a new server object\t", - "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", - "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", - "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", - "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_spec.lua\t", - "Success\t||\topencode.config uses default values when no options are provided\t", - "Success\t||\topencode.config merges user options with defaults\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/keymap_spec.lua\t", - "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", - "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", - "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", - "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", - "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", - "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", - "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", - "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", - "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", - "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", - "Success\t||\topencode.keymap setup_permisson_keymap sets up permission keymaps when there is a current permission\t", - "Success\t||\topencode.keymap setup_permisson_keymap should delete existing permission keymaps if no current permission exists after being set\t", - "Success\t||\topencode.keymap setup_permisson_keymap does not set permission keymaps when there is no current permission\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/state_spec.lua\t", - "Success\t||\topencode.state (observable) notifies listeners on key change\t", - "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", - "Success\t||\topencode.state (observable) can unregister listeners\t", - "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/timer_spec.lua\t", - "Success\t||\tTimer Timer.new creates a new timer with required options\t", - "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", - "Success\t||\tTimer Timer.new stores optional parameters\t", - "Success\t||\tTimer Timer:start starts a repeating timer\t", - "Success\t||\tTimer Timer:start starts a one-shot timer\t", - "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", - "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", - "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", - "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", - "Success\t||\tTimer Timer:start throws error when timer creation fails\t", - "Success\t||\tTimer Timer:stop stops a running timer\t", - "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", - "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", - "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", - "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", - "Success\t||\tTimer Timer:is_running returns true when timer is running\t", - "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", - "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", - "Success\t||\tTimer Integration tests can restart a stopped timer\t", - "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", - "\t", - "Success: \t20\t", - "Failed : \t0\t", - "========================================\t", - "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", - "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", - "", - "========================================\t", - "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua\t", - "Success\t||\trenderer replays api-error correctly (event-by-event)\t", - "Success\t||\trenderer replays api-error correctly (session)\t", - "Success\t||\trenderer replays diff correctly (event-by-event)\t", - "Success\t||\trenderer replays diff correctly (session)\t", - "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event)\t", - "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", - "Success\t||\trenderer replays message-removal correctly (event-by-event)\t", - "Success\t||\trenderer replays permission-denied correctly (event-by-event)\t", - "Success\t||\trenderer replays permission-denied correctly (session)\t", - "Success\t||\trenderer replays permission-prompt correctly (event-by-event)\t", - "Success\t||\trenderer replays permission correctly (event-by-event)\t", - "Success\t||\trenderer replays permission correctly (session)\t", - "Success\t||\trenderer replays planning correctly (event-by-event)\t", - "Success\t||\trenderer replays planning correctly (session)\t", - "Success\t||\trenderer replays redo-all correctly (event-by-event)\t", - "Success\t||\trenderer replays redo-all correctly (session)\t", - "Success\t||\trenderer replays redo-once correctly (event-by-event)\t", - "Success\t||\trenderer replays redo-once correctly (session)\t", - "Success\t||\trenderer replays revert correctly (event-by-event)\t", - "Success\t||\trenderer replays revert correctly (session)\t", - "Success\t||\trenderer replays selection correctly (event-by-event)\t", - "Success\t||\trenderer replays selection correctly (session)\t", - "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event)\t", - "Success\t||\trenderer replays simple-session correctly (event-by-event)\t", - "Success\t||\trenderer replays simple-session correctly (session)\t", - "Success\t||\trenderer replays tool-invalid correctly (event-by-event)\t", - "Success\t||\trenderer replays tool-invalid correctly (session)\t", - "Success\t||\trenderer replays updating-text correctly (event-by-event)\t", - "Success\t||\trenderer replays updating-text correctly (session)\t", - "Success\t||\trenderer unrendered messages is_message_unrendered returns true for unrendered message\t", - "Success\t||\trenderer unrendered messages is_message_unrendered returns false for rendered message\t", - "Success\t||\trenderer unrendered messages is_message_unrendered returns false for nil\t", - "Fail\t||\trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", - " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: Expected objects to not be the same.", - " Passed in:", - " (nil)", - " Did not expect:", - " type nil", - " ", - " stack traceback:", - " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:201>", - " \t", - "Success\t||\trenderer unrendered messages _replace_message_in_buffer returns false for unrendered message\t", - "Fail\t||\trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", - " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: Expected objects to be equal.", - " Passed in:", - " (number) 9", - " Expected:", - " (number) 15", - " ", - " stack traceback:", - " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:222>", - " \t", - "\t", - "Success: \t33\t", - "Failed : \t2\t", - "========================================\t", - "Tests Failed. Exit: 1\t", - "✗ Unit tests failed", - "------------------------------------------------", - "", - "======== TEST FAILURES SUMMARY ========", - "Found 2 failing test(s):", - "", - "FAILED TEST: \trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", - "FAILED TEST: \trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", - "", - "`````", - "", - "" - ], + "actions": [], "extmarks": [ [ 1, 1, 0, { - "right_gravity": true, - "virt_text_hide": false, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-23 21:00:24)", "OpencodeHint"], - [" [msg_a12df6fcc002lSmBoztX2X6eCp]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-23 21:00:24)", + "OpencodeHint" + ], + [ + " [msg_a12df6fcc002lSmBoztX2X6eCp]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -439,14 +45,19 @@ 3, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -454,14 +65,19 @@ 4, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -469,14 +85,19 @@ 5, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -484,14 +105,19 @@ 6, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -499,14 +125,19 @@ 7, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -514,14 +145,19 @@ 8, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -529,14 +165,19 @@ 9, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -544,14 +185,19 @@ 10, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -559,14 +205,19 @@ 11, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -574,14 +225,19 @@ 12, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -589,14 +245,19 @@ 13, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -604,14 +265,19 @@ 14, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -619,14 +285,19 @@ 15, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -634,14 +305,19 @@ 16, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -649,14 +325,19 @@ 17, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -664,14 +345,19 @@ 18, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -679,14 +365,19 @@ 19, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -694,14 +385,19 @@ 20, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -709,14 +405,19 @@ 21, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -724,14 +425,19 @@ 22, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -739,14 +445,19 @@ 23, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -754,14 +465,19 @@ 24, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -769,14 +485,19 @@ 25, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -784,14 +505,19 @@ 26, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -799,14 +525,19 @@ 27, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -814,14 +545,19 @@ 28, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -829,14 +565,19 @@ 29, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -844,14 +585,19 @@ 30, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -859,14 +605,19 @@ 31, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -874,14 +625,19 @@ 32, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -889,14 +645,19 @@ 33, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -904,14 +665,19 @@ 34, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -919,14 +685,19 @@ 35, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -934,14 +705,19 @@ 36, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -949,14 +725,19 @@ 37, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -964,14 +745,19 @@ 38, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -979,14 +765,19 @@ 39, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -994,14 +785,19 @@ 40, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1009,14 +805,19 @@ 41, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1024,14 +825,19 @@ 42, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1039,14 +845,19 @@ 43, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1054,14 +865,19 @@ 44, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1069,14 +885,19 @@ 45, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1084,14 +905,19 @@ 46, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1099,14 +925,19 @@ 47, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1114,14 +945,19 @@ 48, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1129,14 +965,19 @@ 49, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1144,14 +985,19 @@ 50, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1159,14 +1005,19 @@ 51, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1174,14 +1025,19 @@ 52, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1189,14 +1045,19 @@ 53, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1204,14 +1065,19 @@ 54, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1219,14 +1085,19 @@ 55, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1234,14 +1105,19 @@ 56, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1249,14 +1125,19 @@ 57, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1264,14 +1145,19 @@ 58, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1279,14 +1165,19 @@ 59, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1294,14 +1185,19 @@ 60, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1309,14 +1205,19 @@ 61, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1324,14 +1225,19 @@ 62, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1339,14 +1245,19 @@ 63, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1354,14 +1265,19 @@ 64, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1369,14 +1285,19 @@ 65, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1384,14 +1305,19 @@ 66, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1399,14 +1325,19 @@ 67, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1414,14 +1345,19 @@ 68, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1429,14 +1365,19 @@ 69, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1444,14 +1385,19 @@ 70, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1459,14 +1405,19 @@ 71, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1474,14 +1425,19 @@ 72, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1489,14 +1445,19 @@ 73, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1504,14 +1465,19 @@ 74, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1519,14 +1485,19 @@ 75, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1534,14 +1505,19 @@ 76, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1549,14 +1525,19 @@ 77, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1564,14 +1545,19 @@ 78, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1579,14 +1565,19 @@ 79, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1594,14 +1585,19 @@ 80, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1609,14 +1605,19 @@ 81, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1624,14 +1625,19 @@ 82, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1639,14 +1645,19 @@ 83, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1654,14 +1665,19 @@ 84, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1669,14 +1685,19 @@ 85, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1684,14 +1705,19 @@ 86, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1699,14 +1725,19 @@ 87, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1714,14 +1745,19 @@ 88, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1729,14 +1765,19 @@ 89, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1744,14 +1785,19 @@ 90, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1759,14 +1805,19 @@ 91, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1774,14 +1825,19 @@ 92, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1789,14 +1845,19 @@ 93, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1804,14 +1865,19 @@ 94, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1819,14 +1885,19 @@ 95, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1834,14 +1905,19 @@ 96, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1849,14 +1925,19 @@ 97, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1864,14 +1945,19 @@ 98, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1879,14 +1965,19 @@ 99, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1894,14 +1985,19 @@ 100, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1909,14 +2005,19 @@ 101, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1924,14 +2025,19 @@ 102, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1939,14 +2045,19 @@ 103, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1954,14 +2065,19 @@ 104, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1969,14 +2085,19 @@ 105, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1984,14 +2105,19 @@ 106, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -1999,14 +2125,19 @@ 107, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2014,14 +2145,19 @@ 108, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2029,14 +2165,19 @@ 109, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2044,14 +2185,19 @@ 110, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2059,14 +2205,19 @@ 111, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2074,14 +2225,19 @@ 112, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2089,14 +2245,19 @@ 113, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2104,14 +2265,19 @@ 114, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2119,14 +2285,19 @@ 115, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2134,14 +2305,19 @@ 116, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2149,14 +2325,19 @@ 117, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2164,14 +2345,19 @@ 118, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2179,14 +2365,19 @@ 119, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2194,14 +2385,19 @@ 120, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2209,14 +2405,19 @@ 121, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2224,14 +2425,19 @@ 122, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2239,14 +2445,19 @@ 123, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2254,14 +2465,19 @@ 124, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2269,14 +2485,19 @@ 125, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2284,14 +2505,19 @@ 126, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2299,14 +2525,19 @@ 127, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2314,14 +2545,19 @@ 128, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2329,14 +2565,19 @@ 129, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2344,14 +2585,19 @@ 130, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2359,14 +2605,19 @@ 131, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2374,14 +2625,19 @@ 132, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2389,14 +2645,19 @@ 133, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2404,14 +2665,19 @@ 134, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2419,14 +2685,19 @@ 135, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2434,14 +2705,19 @@ 136, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2449,14 +2725,19 @@ 137, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2464,14 +2745,19 @@ 138, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2479,14 +2765,19 @@ 139, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2494,14 +2785,19 @@ 140, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2509,14 +2805,19 @@ 141, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2524,14 +2825,19 @@ 142, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2539,14 +2845,19 @@ 143, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2554,14 +2865,19 @@ 144, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2569,14 +2885,19 @@ 145, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2584,14 +2905,19 @@ 146, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2599,14 +2925,19 @@ 147, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2614,14 +2945,19 @@ 148, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2629,14 +2965,19 @@ 149, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2644,14 +2985,19 @@ 150, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2659,14 +3005,19 @@ 151, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2674,14 +3025,19 @@ 152, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2689,14 +3045,19 @@ 153, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2704,14 +3065,19 @@ 154, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2719,14 +3085,19 @@ 155, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2734,14 +3105,19 @@ 156, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2749,14 +3125,19 @@ 157, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2764,14 +3145,19 @@ 158, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2779,14 +3165,19 @@ 159, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2794,14 +3185,19 @@ 160, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2809,14 +3205,19 @@ 161, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2824,14 +3225,19 @@ 162, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2839,14 +3245,19 @@ 163, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2854,14 +3265,19 @@ 164, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2869,14 +3285,19 @@ 165, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2884,14 +3305,19 @@ 166, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2899,14 +3325,19 @@ 167, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2914,14 +3345,19 @@ 168, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2929,14 +3365,19 @@ 169, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2944,14 +3385,19 @@ 170, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2959,14 +3405,19 @@ 171, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2974,14 +3425,19 @@ 172, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -2989,14 +3445,19 @@ 173, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3004,14 +3465,19 @@ 174, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3019,14 +3485,19 @@ 175, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3034,14 +3505,19 @@ 176, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3049,14 +3525,19 @@ 177, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3064,14 +3545,19 @@ 178, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3079,14 +3565,19 @@ 179, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3094,14 +3585,19 @@ 180, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3109,14 +3605,19 @@ 181, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3124,14 +3625,19 @@ 182, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3139,14 +3645,19 @@ 183, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3154,14 +3665,19 @@ 184, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3169,14 +3685,19 @@ 185, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3184,14 +3705,19 @@ 186, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3199,14 +3725,19 @@ 187, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3214,14 +3745,19 @@ 188, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3229,14 +3765,19 @@ 189, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3244,14 +3785,19 @@ 190, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3259,14 +3805,19 @@ 191, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3274,14 +3825,19 @@ 192, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3289,14 +3845,19 @@ 193, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3304,14 +3865,19 @@ 194, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3319,14 +3885,19 @@ 195, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3334,14 +3905,19 @@ 196, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3349,14 +3925,19 @@ 197, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3364,14 +3945,19 @@ 198, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3379,14 +3965,19 @@ 199, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3394,14 +3985,19 @@ 200, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3409,14 +4005,19 @@ 201, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3424,14 +4025,19 @@ 202, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3439,14 +4045,19 @@ 203, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3454,14 +4065,19 @@ 204, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3469,14 +4085,19 @@ 205, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3484,14 +4105,19 @@ 206, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3499,14 +4125,19 @@ 207, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3514,14 +4145,19 @@ 208, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3529,14 +4165,19 @@ 209, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3544,14 +4185,19 @@ 210, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3559,14 +4205,19 @@ 211, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3574,14 +4225,19 @@ 212, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3589,14 +4245,19 @@ 213, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3604,14 +4265,19 @@ 214, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3619,14 +4285,19 @@ 215, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3634,14 +4305,19 @@ 216, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3649,14 +4325,19 @@ 217, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3664,14 +4345,19 @@ 218, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3679,14 +4365,19 @@ 219, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3694,14 +4385,19 @@ 220, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3709,14 +4405,19 @@ 221, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3724,14 +4425,19 @@ 222, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3739,14 +4445,19 @@ 223, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3754,14 +4465,19 @@ 224, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3769,14 +4485,19 @@ 225, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3784,14 +4505,19 @@ 226, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3799,14 +4525,19 @@ 227, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3814,14 +4545,19 @@ 228, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3829,14 +4565,19 @@ 229, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3844,14 +4585,19 @@ 230, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3859,14 +4605,19 @@ 231, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3874,14 +4625,19 @@ 232, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3889,14 +4645,19 @@ 233, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3904,14 +4665,19 @@ 234, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3919,14 +4685,19 @@ 235, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3934,14 +4705,19 @@ 236, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3949,14 +4725,19 @@ 237, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3964,14 +4745,19 @@ 238, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3979,14 +4765,19 @@ 239, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3994,14 +4785,19 @@ 240, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4009,14 +4805,19 @@ 241, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4024,14 +4825,19 @@ 242, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4039,14 +4845,19 @@ 243, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4054,14 +4865,19 @@ 244, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4069,14 +4885,19 @@ 245, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4084,14 +4905,19 @@ 246, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4099,14 +4925,19 @@ 247, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4114,14 +4945,19 @@ 248, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4129,14 +4965,19 @@ 249, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4144,14 +4985,19 @@ 250, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4159,14 +5005,19 @@ 251, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4174,14 +5025,19 @@ 252, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4189,14 +5045,19 @@ 253, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4204,14 +5065,19 @@ 254, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4219,14 +5085,19 @@ 255, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4234,14 +5105,19 @@ 256, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4249,14 +5125,19 @@ 257, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4264,14 +5145,19 @@ 258, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4279,14 +5165,19 @@ 259, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4294,14 +5185,19 @@ 260, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4309,14 +5205,19 @@ 261, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4324,14 +5225,19 @@ 262, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4339,14 +5245,19 @@ 263, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4354,14 +5265,19 @@ 264, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4369,14 +5285,19 @@ 265, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4384,14 +5305,19 @@ 266, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4399,14 +5325,19 @@ 267, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4414,14 +5345,19 @@ 268, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4429,14 +5365,19 @@ 269, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4444,14 +5385,19 @@ 270, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4459,14 +5405,19 @@ 271, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4474,14 +5425,19 @@ 272, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4489,14 +5445,19 @@ 273, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4504,14 +5465,19 @@ 274, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4519,14 +5485,19 @@ 275, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4534,14 +5505,19 @@ 276, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4549,14 +5525,19 @@ 277, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4564,14 +5545,19 @@ 278, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4579,14 +5565,19 @@ 279, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4594,14 +5585,19 @@ 280, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4609,14 +5605,19 @@ 281, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4624,14 +5625,19 @@ 282, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4639,14 +5645,19 @@ 283, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4654,14 +5665,19 @@ 284, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4669,14 +5685,19 @@ 285, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4684,14 +5705,19 @@ 286, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4699,14 +5725,19 @@ 287, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4714,14 +5745,19 @@ 288, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4729,14 +5765,19 @@ 289, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4744,14 +5785,19 @@ 290, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4759,14 +5805,19 @@ 291, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4774,14 +5825,19 @@ 292, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4789,14 +5845,19 @@ 293, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4804,14 +5865,19 @@ 294, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4819,14 +5885,19 @@ 295, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4834,14 +5905,19 @@ 296, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4849,14 +5925,19 @@ 297, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4864,14 +5945,19 @@ 298, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4879,14 +5965,19 @@ 299, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4894,14 +5985,19 @@ 300, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4909,14 +6005,19 @@ 301, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4924,14 +6025,19 @@ 302, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4939,14 +6045,19 @@ 303, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4954,14 +6065,19 @@ 304, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4969,14 +6085,19 @@ 305, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4984,14 +6105,19 @@ 306, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -4999,14 +6125,19 @@ 307, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5014,14 +6145,19 @@ 308, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5029,14 +6165,19 @@ 309, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5044,14 +6185,19 @@ 310, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5059,14 +6205,19 @@ 311, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5074,14 +6225,19 @@ 312, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5089,14 +6245,19 @@ 313, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5104,14 +6265,19 @@ 314, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5119,14 +6285,19 @@ 315, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5134,14 +6305,19 @@ 316, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5149,14 +6325,19 @@ 317, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5164,14 +6345,19 @@ 318, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5179,14 +6365,19 @@ 319, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5194,14 +6385,19 @@ 320, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5209,14 +6405,19 @@ 321, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5224,14 +6425,19 @@ 322, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5239,14 +6445,19 @@ 323, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5254,14 +6465,19 @@ 324, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5269,14 +6485,19 @@ 325, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5284,14 +6505,19 @@ 326, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5299,14 +6525,19 @@ 327, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5314,14 +6545,19 @@ 328, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5329,14 +6565,19 @@ 329, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5344,14 +6585,19 @@ 330, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5359,14 +6605,19 @@ 331, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5374,14 +6625,19 @@ 332, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5389,14 +6645,19 @@ 333, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5404,14 +6665,19 @@ 334, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5419,14 +6685,19 @@ 335, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5434,14 +6705,19 @@ 336, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5449,14 +6725,19 @@ 337, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5464,14 +6745,19 @@ 338, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5479,14 +6765,19 @@ 339, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5494,14 +6785,19 @@ 340, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5509,14 +6805,19 @@ 341, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5524,14 +6825,19 @@ 342, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5539,14 +6845,19 @@ 343, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5554,14 +6865,19 @@ 344, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5569,14 +6885,19 @@ 345, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5584,14 +6905,19 @@ 346, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5599,14 +6925,19 @@ 347, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5614,14 +6945,19 @@ 348, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5629,14 +6965,19 @@ 349, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5644,14 +6985,19 @@ 350, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5659,14 +7005,19 @@ 351, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5674,14 +7025,19 @@ 352, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5689,14 +7045,19 @@ 353, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5704,14 +7065,19 @@ 354, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5719,14 +7085,19 @@ 355, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5734,14 +7105,19 @@ 356, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5749,14 +7125,19 @@ 357, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5764,14 +7145,19 @@ 358, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5779,14 +7165,19 @@ 359, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5794,14 +7185,19 @@ 360, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5809,14 +7205,19 @@ 361, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5824,14 +7225,19 @@ 362, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5839,14 +7245,19 @@ 363, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5854,14 +7265,19 @@ 364, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5869,14 +7285,19 @@ 365, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5884,14 +7305,19 @@ 366, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5899,14 +7325,19 @@ 367, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5914,14 +7345,19 @@ 368, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5929,14 +7365,19 @@ 369, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5944,14 +7385,19 @@ 370, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5959,14 +7405,19 @@ 371, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5974,14 +7425,19 @@ 372, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5989,14 +7445,19 @@ 373, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6004,14 +7465,19 @@ 374, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6019,14 +7485,19 @@ 375, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6034,14 +7505,19 @@ 376, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6049,14 +7525,19 @@ 377, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6064,14 +7545,19 @@ 378, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6079,14 +7565,19 @@ 379, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6094,14 +7585,19 @@ 380, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6109,14 +7605,19 @@ 381, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6124,14 +7625,19 @@ 382, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6139,14 +7645,19 @@ 383, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6154,14 +7665,19 @@ 384, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6169,14 +7685,19 @@ 385, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6184,14 +7705,19 @@ 386, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6199,14 +7725,19 @@ 387, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6214,14 +7745,19 @@ 388, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6229,14 +7765,19 @@ 389, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6244,14 +7785,19 @@ 390, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6259,14 +7805,19 @@ 391, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6274,14 +7825,19 @@ 392, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6289,14 +7845,19 @@ 393, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6304,14 +7865,19 @@ 394, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6319,14 +7885,19 @@ 395, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6334,14 +7905,19 @@ 396, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6349,14 +7925,19 @@ 397, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6364,14 +7945,19 @@ 398, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6379,14 +7965,19 @@ 399, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6394,14 +7985,19 @@ 400, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6409,14 +8005,19 @@ 401, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6424,14 +8025,19 @@ 402, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6439,14 +8045,19 @@ 403, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6454,14 +8065,19 @@ 404, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6469,14 +8085,19 @@ 405, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6484,14 +8105,19 @@ 406, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -6499,17 +8125,433 @@ 407, 0, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ] ], - "timestamp": 1766432122, - "actions": [] -} + "lines": [ + "----", + "", + "", + "** run** `Run all tests to verify refactoring`", + "", + "`````bash", + "> cd /Users/cam/Dev/neovim-dev/opencode.nvim && ./run_tests.sh", + "", + "Running tests for opencode.nvim", + "------------------------------------------------", + "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/minimal/plugin_spec.lua\t", + "Success\t||\topencode.nvim plugin loads the plugin without errors\t", + "Success\t||\topencode.nvim plugin can be set up with custom config\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "✓ Minimal tests passed", + "------------------------------------------------", + "Starting...Scheduling: ./tests/unit/timer_spec.lua", + "Scheduling: ./tests/unit/server_job_spec.lua", + "Scheduling: ./tests/unit/config_spec.lua", + "Scheduling: ./tests/unit/api_spec.lua", + "Scheduling: ./tests/unit/event_manager_spec.lua", + "Scheduling: ./tests/unit/init_spec.lua", + "Scheduling: ./tests/unit/state_spec.lua", + "Scheduling: ./tests/unit/id_spec.lua", + "Scheduling: ./tests/unit/api_client_spec.lua", + "Scheduling: ./tests/unit/context_spec.lua", + "Scheduling: ./tests/unit/session_spec.lua", + "Scheduling: ./tests/unit/config_file_spec.lua", + "Scheduling: ./tests/unit/renderer_spec.lua", + "Scheduling: ./tests/unit/opencode_server_spec.lua", + "Scheduling: ./tests/unit/core_spec.lua", + "Scheduling: ./tests/unit/render_state_spec.lua", + "Scheduling: ./tests/unit/snapshot_spec.lua", + "Scheduling: ./tests/unit/keymap_spec.lua", + "Scheduling: ./tests/unit/util_spec.lua", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/server_job_spec.lua\t", + "Success\t||\tserver_job exposes expected public functions\t", + "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", + "Success\t||\tserver_job call_api rejects on non 2xx\t", + "Success\t||\tserver_job stream_api forwards chunks\t", + "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/util_spec.lua\t", + "Success\t||\tutil.parse_dot_args parses flat booleans\t", + "Success\t||\tutil.parse_dot_args parses nested dot notation\t", + "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", + "Success\t||\tutil.parse_dot_args parses numbers\t", + "Success\t||\tutil.parse_dot_args handles empty string\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_client_spec.lua\t", + "Success\t||\tapi_client should create a new client instance\t", + "Success\t||\tapi_client should remove trailing slash from base_url\t", + "Success\t||\tapi_client should create client using create factory function\t", + "Success\t||\tapi_client should have all expected API methods\t", + "Success\t||\tapi_client should construct URLs correctly with query parameters\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/session_spec.lua\t", + "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", + "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", + "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", + "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", + "Success\t||\topencode.session get_by_name returns the session with matching ID\t", + "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", + "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", + "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", + "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", + "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", + "Success\t||\topencode.session get_messages returns nil when session is nil\t", + "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", + "Success\t||\topencode.session get_messages returns messages with their parts\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_file_spec.lua\t", + "Success\t||\tconfig_file.setup lazily loads config when accessed\t", + "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", + "Success\t||\tconfig_file.setup get_opencode_project returns project\t", + "\t", + "Success: \t3\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/api_spec.lua\t", + "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", + "Success\t||\topencode.api setup registers all commands\t", + "Success\t||\topencode.api setup sets up command functions that call the correct core functions\t", + "Success\t||\topencode.api Lua API provides callable functions that match commands\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/id_spec.lua\t", + "Success\t||\tID module should generate ascending session IDs\t", + "Success\t||\tID module should generate descending message IDs\t", + "Success\t||\tID module should validate given IDs correctly\t", + "Success\t||\tID module should throw error for invalid given IDs\t", + "Success\t||\tID module should validate schemas correctly\t", + "Success\t||\tID module should return available prefixes\t", + "Success\t||\tID module should generate IDs with correct length structure\t", + "\t", + "Success: \t7\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/render_state_spec.lua\t", + "Success\t||\tRenderState new and reset creates a new instance\t", + "Success\t||\tRenderState new and reset resets to empty state\t", + "Success\t||\tRenderState set_message sets a new message\t", + "Success\t||\tRenderState set_message updates line index for message\t", + "Success\t||\tRenderState set_message updates existing message\t", + "Success\t||\tRenderState set_part sets a new part\t", + "Success\t||\tRenderState set_part updates line index for part\t", + "Success\t||\tRenderState set_part initializes actions array\t", + "Success\t||\tRenderState get_part_at_line returns part at line\t", + "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", + "Success\t||\tRenderState get_message_at_line returns message at line\t", + "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", + "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", + "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", + "Success\t||\tRenderState actions adds actions to part\t", + "Success\t||\tRenderState actions adds actions with offset\t", + "Success\t||\tRenderState actions clears actions for part\t", + "Success\t||\tRenderState actions gets actions at line\t", + "Success\t||\tRenderState actions gets all actions from all parts\t", + "Success\t||\tRenderState update_part_lines updates part line positions\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", + "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", + "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", + "Success\t||\tRenderState remove_part clears line index for removed part\t", + "Success\t||\tRenderState remove_part returns false for non-existent part\t", + "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", + "Success\t||\tRenderState remove_message clears line index for removed message\t", + "Success\t||\tRenderState remove_message returns false for non-existent message\t", + "Success\t||\tRenderState remove_message removes unrendered message without shifting\t", + "Success\t||\tRenderState shift_all does nothing when delta is 0\t", + "Success\t||\tRenderState shift_all shifts content at or after from_line\t", + "Success\t||\tRenderState shift_all shifts actions with parts\t", + "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", + "Success\t||\tRenderState shift_all invalidates index when content shifted\t", + "Success\t||\tRenderState shift_all exits early when content found before from_line\t", + "Success\t||\tRenderState update_part_data updates part reference\t", + "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", + "Success\t||\tRenderState get_unrendered_message_ids returns empty list when no unrendered messages\t", + "Success\t||\tRenderState get_unrendered_message_ids returns list of unrendered message IDs\t", + "Success\t||\tRenderState get_unrendered_message_ids returns sorted list of message IDs\t", + "\t", + "Success: \t41\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/init_spec.lua\t", + "Success\t||\topencode has setup function in the public API\t", + "Success\t||\topencode main module can be required without errors\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/snapshot_spec.lua\t", + "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", + "Success\t||\tsnapshot.restore notifies error if no active session\t", + "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", + "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/event_manager_spec.lua\t", + "Success\t||\tEventManager should create a new instance\t", + "Success\t||\tEventManager should subscribe and emit events\t", + "Success\t||\tEventManager should handle multiple subscribers\t", + "Success\t||\tEventManager should unsubscribe correctly\t", + "Success\t||\tEventManager should track subscriber count\t", + "Success\t||\tEventManager should list event names\t", + "Success\t||\tEventManager should handle starting and stopping\t", + "Success\t||\tEventManager should not start multiple times\t", + "\t", + "Success: \t8\t", + "Failed : \t0\t", + "========================================\t", + "Error detected while processing command line:", + "opencode command not found - please install and configure opencode before using this plugin", + "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/core_spec.lua\t", + "Success\t||\topencode.core open creates windows if they don't exist\t", + "Success\t||\topencode.core open handles new session properly\t", + "Success\t||\topencode.core open focuses the appropriate window\t", + "Success\t||\topencode.core select_session filters sessions by description and parentID\t", + "Success\t||\topencode.core send_message sends a message via api_client\t", + "Success\t||\topencode.core send_message creates new session when none active\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", + "\t", + "Success: \t10\t", + "Failed : \t0\t", + "========================================\t", + "File not added to context. Could not read.", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/context_spec.lua\t", + "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", + "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", + "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", + "Success\t||\textract_legacy_tag extracts content between tags\t", + "Success\t||\textract_legacy_tag returns nil if tag not found\t", + "Success\t||\tformat_message returns a parts array with prompt as first part\t", + "Success\t||\tformat_message includes mentioned_files and subagents\t", + "Success\t||\tdelta_context removes current_file if unchanged\t", + "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/opencode_server_spec.lua\t", + "Success\t||\topencode.opencode_server creates a new server object\t", + "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", + "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", + "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", + "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/config_spec.lua\t", + "Success\t||\topencode.config uses default values when no options are provided\t", + "Success\t||\topencode.config merges user options with defaults\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/keymap_spec.lua\t", + "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", + "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", + "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", + "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", + "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", + "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", + "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", + "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", + "Success\t||\topencode.keymap setup_permisson_keymap sets up permission keymaps when there is a current permission\t", + "Success\t||\topencode.keymap setup_permisson_keymap should delete existing permission keymaps if no current permission exists after being set\t", + "Success\t||\topencode.keymap setup_permisson_keymap does not set permission keymaps when there is no current permission\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/state_spec.lua\t", + "Success\t||\topencode.state (observable) notifies listeners on key change\t", + "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", + "Success\t||\topencode.state (observable) can unregister listeners\t", + "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/timer_spec.lua\t", + "Success\t||\tTimer Timer.new creates a new timer with required options\t", + "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", + "Success\t||\tTimer Timer.new stores optional parameters\t", + "Success\t||\tTimer Timer:start starts a repeating timer\t", + "Success\t||\tTimer Timer:start starts a one-shot timer\t", + "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", + "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", + "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", + "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", + "Success\t||\tTimer Timer:start throws error when timer creation fails\t", + "Success\t||\tTimer Timer:stop stops a running timer\t", + "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", + "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", + "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", + "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", + "Success\t||\tTimer Timer:is_running returns true when timer is running\t", + "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", + "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", + "Success\t||\tTimer Integration tests can restart a stopped timer\t", + "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "", + "========================================\t", + "Testing: \t/Users/cam/Dev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua\t", + "Success\t||\trenderer replays api-error correctly (event-by-event)\t", + "Success\t||\trenderer replays api-error correctly (session)\t", + "Success\t||\trenderer replays diff correctly (event-by-event)\t", + "Success\t||\trenderer replays diff correctly (session)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event)\t", + "Success\t||\trenderer replays permission-denied correctly (session)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event)\t", + "Success\t||\trenderer replays permission correctly (event-by-event)\t", + "Success\t||\trenderer replays permission correctly (session)\t", + "Success\t||\trenderer replays planning correctly (event-by-event)\t", + "Success\t||\trenderer replays planning correctly (session)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event)\t", + "Success\t||\trenderer replays redo-all correctly (session)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event)\t", + "Success\t||\trenderer replays redo-once correctly (session)\t", + "Success\t||\trenderer replays revert correctly (event-by-event)\t", + "Success\t||\trenderer replays revert correctly (session)\t", + "Success\t||\trenderer replays selection correctly (event-by-event)\t", + "Success\t||\trenderer replays selection correctly (session)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event)\t", + "Success\t||\trenderer replays simple-session correctly (session)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event)\t", + "Success\t||\trenderer replays tool-invalid correctly (session)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event)\t", + "Success\t||\trenderer replays updating-text correctly (session)\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns true for unrendered message\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns false for rendered message\t", + "Success\t||\trenderer unrendered messages is_message_unrendered returns false for nil\t", + "Fail\t||\trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", + " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: Expected objects to not be the same.", + " Passed in:", + " (nil)", + " Did not expect:", + " type nil", + " ", + " stack traceback:", + " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:207: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:201>", + " \t", + "Success\t||\trenderer unrendered messages _replace_message_in_buffer returns false for unrendered message\t", + "Fail\t||\trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", + " ...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: Expected objects to be equal.", + " Passed in:", + " (number) 9", + " Expected:", + " (number) 15", + " ", + " stack traceback:", + " \t...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:232: in function <...ev/neovim-dev/opencode.nvim/tests/unit/renderer_spec.lua:222>", + " \t", + "\t", + "Success: \t33\t", + "Failed : \t2\t", + "========================================\t", + "Tests Failed. Exit: 1\t", + "✗ Unit tests failed", + "------------------------------------------------", + "", + "======== TEST FAILURES SUMMARY ========", + "Found 2 failing test(s):", + "", + "FAILED TEST: \trenderer unrendered messages _remove_message_from_buffer skips unrendered message\t", + "FAILED TEST: \trenderer unrendered messages _rerender_part skips if parent message is unrendered\t", + "", + "`````", + "", + "" + ], + "timestamp": 1770748573 +} \ No newline at end of file diff --git a/tests/data/api-abort.expected.json b/tests/data/api-abort.expected.json index 7998b564..2d65695b 100644 --- a/tests/data/api-abort.expected.json +++ b/tests/data/api-abort.expected.json @@ -1,26 +1,43 @@ { - "timestamp": 1766432123, + "actions": [], "extmarks": [ [ 1, 1, 0, { - "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-27 22:44:29)", "OpencodeHint"], - [" [msg_a27d8299d001nchmBunYlZcPyL]", "OpencodeHint"] - ], - "virt_text_repeat_linebreak": false, "ns_id": 3, + "priority": 10, "right_gravity": true, + "virt_text": [ + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-27 22:44:29)", + "OpencodeHint" + ], + [ + " [msg_a27d8299d001nchmBunYlZcPyL]", + "OpencodeHint" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -28,14 +45,19 @@ 2, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -43,14 +65,19 @@ 3, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -58,14 +85,19 @@ 4, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -73,14 +105,19 @@ 5, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -88,25 +125,41 @@ 8, 0, { - "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-10-27 22:44:29)", "OpencodeHint"], - [" [msg_a27d829f9002sfUFPslHq5P2b4]", "OpencodeHint"] - ], - "virt_text_repeat_linebreak": false, "ns_id": 3, + "priority": 10, "right_gravity": true, + "virt_text": [ + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-10-27 22:44:29)", + "OpencodeHint" + ], + [ + " [msg_a27d829f9002sfUFPslHq5P2b4]", + "OpencodeHint" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [], "lines": [ "----", "", @@ -123,5 +176,6 @@ "> [!ERROR] The operation was aborted.", "", "" - ] -} + ], + "timestamp": 1770748573 +} \ No newline at end of file diff --git a/tests/data/api-error.expected.json b/tests/data/api-error.expected.json index be5e37a7..808c7815 100644 --- a/tests/data/api-error.expected.json +++ b/tests/data/api-error.expected.json @@ -1,27 +1,4 @@ { - "lines": [ - "----", - "", - "", - "test 3", - "", - "[`diff-test.txt`](diff-test.txt)", - "", - "----", - "", - "", - "This is some sample text", - "", - "> [!ERROR] Simulated: tool/file read failed for earlier assistant message", - "", - "----", - "", - "", - "> [!ERROR] AI_APICallError: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.", - "", - "" - ], - "timestamp": 1766432123, "actions": [], "extmarks": [ [ @@ -29,21 +6,38 @@ 1, 0, { - "virt_text_hide": false, + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 04:44:37)", "OpencodeHint"], - [" [msg_9ffef0129001CoCrBKemk7DqcU]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 04:44:37)", + "OpencodeHint" + ], + [ + " [msg_9ffef0129001CoCrBKemk7DqcU]", + "OpencodeHint" + ] ], - "virt_text_repeat_linebreak": false, - "virt_text_win_col": -3, - "priority": 10, + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -51,14 +45,19 @@ 2, 0, { - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -66,14 +65,19 @@ 3, 0, { - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -81,14 +85,19 @@ 4, 0, { - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -96,14 +105,19 @@ 5, 0, { - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_repeat_linebreak": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -111,21 +125,38 @@ 8, 0, { - "virt_text_hide": false, + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4-5-20250929", "OpencodeHint"], - [" (2025-10-20 04:44:37)", "OpencodeHint"], - [" [msg_9ffef0160001eArLyAssT]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4-5-20250929", + "OpencodeHint" + ], + [ + " (2025-10-20 04:44:37)", + "OpencodeHint" + ], + [ + " [msg_9ffef0160001eArLyAssT]", + "OpencodeHint" + ] ], - "virt_text_repeat_linebreak": false, - "virt_text_win_col": -3, - "priority": 10, + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -133,22 +164,62 @@ 15, 0, { - "virt_text_hide": false, + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4-5-20250929", "OpencodeHint"], - [" (2025-10-20 04:44:37)", "OpencodeHint"], - [" [msg_9ffef0170001s2OM00h2cDa94A]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4-5-20250929", + "OpencodeHint" + ], + [ + " (2025-10-20 04:44:37)", + "OpencodeHint" + ], + [ + " [msg_9ffef0170001s2OM00h2cDa94A]", + "OpencodeHint" + ] ], - "virt_text_repeat_linebreak": false, - "virt_text_win_col": -3, - "priority": 10, + "virt_text_hide": false, "virt_text_pos": "win_col", - "ns_id": 3, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] - ] -} + ], + "lines": [ + "----", + "", + "", + "test 3", + "", + "[`diff-test.txt`](diff-test.txt)", + "", + "----", + "", + "", + "This is some sample text", + "", + "> [!ERROR] Simulated: tool/file read failed for earlier assistant message", + "", + "----", + "", + "", + "> [!ERROR] AI_APICallError: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.", + "", + "" + ], + "timestamp": 1770748573 +} \ No newline at end of file diff --git a/tests/data/cursor_data.expected.json b/tests/data/cursor_data.expected.json index 22ebcc0b..49a6b011 100644 --- a/tests/data/cursor_data.expected.json +++ b/tests/data/cursor_data.expected.json @@ -1,54 +1,43 @@ { - "lines": [ - "----", - "", - "", - "what is this doing", - "", - "[`lua/opencode/context.lua`](lua/opencode/context.lua)", - "", - "Line 59:", - "`````lua", - "local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')", - "`````", - "", - "----", - "", - "", - "**Explanation**", - "", - "- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in  `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.", - "- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.", - "- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:", - " - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.", - " - Otherwise the `is_enabled` value (from `config`) is returned.", - "- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.", - "- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.", - "", - "" - ], - "timestamp": 1769105141, + "actions": [], "extmarks": [ [ 1, 1, 0, { + "ns_id": 3, "priority": 10, - "virt_text_repeat_linebreak": false, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-30 17:42:54)", "OpencodeHint"], - [" [msg_a3637244a001FDRDfoBYVPEGpd]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-30 17:42:54)", + "OpencodeHint" + ], + [ + " [msg_a3637244a001FDRDfoBYVPEGpd]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -56,14 +45,19 @@ 2, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -71,14 +65,19 @@ 3, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -86,14 +85,19 @@ 4, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -101,14 +105,19 @@ 5, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -116,14 +125,19 @@ 6, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -131,14 +145,19 @@ 7, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -146,14 +165,19 @@ 8, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -161,14 +185,19 @@ 9, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -176,14 +205,19 @@ 10, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -191,14 +225,19 @@ 11, 0, { + "ns_id": 3, "priority": 4096, - "virt_text_repeat_linebreak": true, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -206,23 +245,68 @@ 13, 0, { + "ns_id": 3, "priority": 10, - "virt_text_repeat_linebreak": false, "right_gravity": true, - "virt_text_win_col": -3, - "ns_id": 3, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-30 17:42:56)", "OpencodeHint"], - [" [msg_a36372b9a001M1lQEix4SK5QE5]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-30 17:42:56)", + "OpencodeHint" + ], + [ + " [msg_a36372b9a001M1lQEix4SK5QE5]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_hide": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [] -} + "lines": [ + "----", + "", + "", + "what is this doing", + "", + "[`lua/opencode/context.lua`](lua/opencode/context.lua)", + "", + "Line 59:", + "`````lua", + "local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')", + "`````", + "", + "----", + "", + "", + "**Explanation**", + "", + "- The line `local is_enabled = vim.tbl_get(config, 'context', context_key, 'enabled')` (in  `lua/opencode/context.lua:58`) uses `vim.tbl_get` to safely read a nested field from a table.", + "- Concretely it attempts to read `config.context[context_key].enabled` but without throwing an error if `config.context` or `config.context[context_key]` is nil. If any intermediate key is missing it returns `nil`.", + "- In the surrounding function `M.is_context_enabled`, that value is the default config value for the given context key. The function then checks the state override:", + " - If `state.current_context_config[context_key].enabled` is not `nil`, that state value (true/false) is returned.", + " - Otherwise the `is_enabled` value (from `config`) is returned.", + "- Example: if `config.context.selection.enabled == true` but `state.current_context_config.selection.enabled == false`, the function returns `false` (state overrides config). If the state value is `nil`, the config value is used.", + "- Why this matters: `vim.tbl_get` provides safe nested access; using it avoids runtime errors when some parts of the nested config are absent.", + "", + "" + ], + "timestamp": 1770748573 +} \ No newline at end of file diff --git a/tests/data/diagnostics.expected.json b/tests/data/diagnostics.expected.json index b4e0e451..bfebbd9a 100644 --- a/tests/data/diagnostics.expected.json +++ b/tests/data/diagnostics.expected.json @@ -1,593 +1,83 @@ { - "lines": [ - "----", - "", - "", - "help me fix the file", - "", - "[`lua/opencode/core.lua`](lua/opencode/core.lua)", - "", - "**Diagnostics:**  (3)", - "", - "----", - "", - "", - "Let's address the three EmmyLua diagnostics at line 130 in  `lua/opencode/core.lua`:", - "", - "### 1. `param-type-not-match`: expected `string` but found `string?`", - "- **Location:** `state.active_session.id` (line 130, col 20)", - "- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.", - "- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.", - "", - "### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`", - "- **Location:** `params` argument to `:create_message` (line 130, col 45)", - "- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.", - "- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.", - "", - "### 3. `need-check-nil`: `state.active_session may be nil`", - "- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.", - "", - "---", - "", - "## Step-by-step Fix Plan", - "", - "```markdown", - "- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", - "- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", - "- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.", - "```", - "", - "Let's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.", - "", - "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", - "", - "`````lua", - " ", - " params.parts = context.format_message(prompt, opts.context)", - " M.before_run(opts)", - " ", - " if not state.active_session or not state.active_session.id then", - " vim.notify('No active session to send message to', vim.log.levels.ERROR)", - " return", - " end", - " state.api_client", - " :create_message(state.active_session.id, params)", - " :and_then(function(response)", - " if not response or not response.info or not response.parts then", - "", - "`````", - "", - "**󰻛 Created Snapshot** `8e790371`", - "", - "----", - "", - "", - "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.", - "", - "Now, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", - "", - "Currently, the code is:", - "```lua", - "if opts.model then", - " local provider, model = opts.model:match('^(.-)/(.+)$')", - " params.model = { providerID = provider, modelID = model }", - " state.current_model = opts.model", - "end", - "```", - "The expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.", - "", - "I'll fix this now.", - "", - "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", - "", - "`````lua", - " local params = {}", - " ", - " if opts.model then", - " local provider, model = opts.model:match('^(.-)/(.+)$')", - " params.model = { providerID = provider, modelID = model }", - " params.model = { modelID = model, providerID = provider }", - " state.current_model = opts.model", - " end", - " ", - " if opts.agent then", - "", - "`````", - "", - "**󰻛 Created Snapshot** `f33f38a7`", - "", - "----", - "", - "", - "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.", - "", - "Now, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.", - "", - "I'll run the test suite using the provided script to verify everything is working.", - "", - "** run** `Run all tests to verify code changes`", - "", - "`````bash", - "> ./run_tests.sh", - "", - "Running tests for opencode.nvim", - "------------------------------------------------", - "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t", - "Success\t||\topencode.nvim plugin loads the plugin without errors\t", - "Success\t||\topencode.nvim plugin can be set up with custom config\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "✓ Minimal tests passed", - "------------------------------------------------", - "Starting...Scheduling: ./tests/unit/keymap_spec.lua", - "Scheduling: ./tests/unit/opencode_server_spec.lua", - "Scheduling: ./tests/unit/context_completion_spec.lua", - "Scheduling: ./tests/unit/config_file_spec.lua", - "Scheduling: ./tests/unit/init_spec.lua", - "Scheduling: ./tests/unit/state_spec.lua", - "Scheduling: ./tests/unit/render_state_spec.lua", - "Scheduling: ./tests/unit/server_job_spec.lua", - "Scheduling: ./tests/unit/config_spec.lua", - "Scheduling: ./tests/unit/context_bar_spec.lua", - "Scheduling: ./tests/unit/renderer_spec.lua", - "Scheduling: ./tests/unit/api_client_spec.lua", - "Scheduling: ./tests/unit/core_spec.lua", - "Scheduling: ./tests/unit/session_spec.lua", - "Scheduling: ./tests/unit/event_manager_spec.lua", - "Scheduling: ./tests/unit/api_spec.lua", - "Scheduling: ./tests/unit/context_spec.lua", - "Scheduling: ./tests/unit/timer_spec.lua", - "Scheduling: ./tests/unit/util_spec.lua", - "Scheduling: ./tests/unit/snapshot_spec.lua", - "Scheduling: ./tests/unit/id_spec.lua", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t", - "Success\t||\tcontext completion get_source should return a completion source\t", - "Success\t||\tcontext completion complete function should return empty table when trigger char does not match\t", - "Success\t||\tcontext completion complete function should return context items when trigger char matches\t", - "Success\t||\tcontext completion complete function should include selection items when selections exist\t", - "Success\t||\tcontext completion complete function should include mentioned files when they exist\t", - "Success\t||\tcontext completion complete function should include subagent items when they exist\t", - "Success\t||\tcontext completion complete function should filter items based on input\t", - "Success\t||\tcontext completion complete function should sort items with available items first\t", - "Success\t||\tcontext completion on_complete function should handle nil item gracefully\t", - "Success\t||\tcontext completion on_complete function should handle item without data gracefully\t", - "Success\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t", - "Success\t||\tcontext completion on_complete function should remove mentioned file when selected\t", - "Success\t||\tcontext completion on_complete function should remove subagent when selected\t", - "Success\t||\tcontext completion on_complete function should remove selection when selection item selected\t", - "Success\t||\tcontext completion on_complete function should clean up trigger character from current line\t", - "Success\t||\tcontext completion formatting functions should format diagnostics correctly\t", - "Success\t||\tcontext completion formatting functions should format cursor data correctly\t", - "Success\t||\tcontext completion formatting functions should format selection correctly\t", - "Success\t||\tcontext completion edge cases should handle empty context gracefully\t", - "Success\t||\tcontext completion edge cases should handle disabled contexts correctly\t", - "\t", - "Success: \t20\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t", - "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", - "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", - "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", - "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", - "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", - "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", - "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", - "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", - "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", - "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", - "Success\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t", - "Success\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t", - "Success\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t", - "Success\t||\topencode.opencode_server creates a new server object\t", - "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", - "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", - "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", - "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t", - "Success\t||\tconfig_file.setup lazily loads config when accessed\t", - "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", - "Success\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t", - "Success\t||\tconfig_file.setup get_opencode_project returns project\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t", - "Success\t||\topencode has setup function in the public API\t", - "Success\t||\topencode main module can be required without errors\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t", - "Success\t||\tRenderState new and reset creates a new instance\t", - "Success\t||\tRenderState new and reset resets to empty state\t", - "Success\t||\tRenderState set_message sets a new message\t", - "Success\t||\tRenderState set_message updates line index for message\t", - "Success\t||\tRenderState set_message updates existing message\t", - "Success\t||\tRenderState set_part sets a new part\t", - "Success\t||\tRenderState set_part updates line index for part\t", - "Success\t||\tRenderState set_part initializes actions array\t", - "Success\t||\tRenderState get_part_at_line returns part at line\t", - "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", - "Success\t||\tRenderState get_message_at_line returns message at line\t", - "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", - "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", - "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", - "Success\t||\tRenderState actions adds actions to part\t", - "Success\t||\tRenderState actions adds actions with offset\t", - "Success\t||\tRenderState actions clears actions for part\t", - "Success\t||\tRenderState actions gets actions at line\t", - "Success\t||\tRenderState actions gets all actions from all parts\t", - "Success\t||\tRenderState update_part_lines updates part line positions\t", - "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", - "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", - "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", - "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", - "Success\t||\tRenderState remove_part clears line index for removed part\t", - "Success\t||\tRenderState remove_part returns false for non-existent part\t", - "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", - "Success\t||\tRenderState remove_message clears line index for removed message\t", - "Success\t||\tRenderState remove_message returns false for non-existent message\t", - "Success\t||\tRenderState shift_all does nothing when delta is 0\t", - "Success\t||\tRenderState shift_all shifts content at or after from_line\t", - "Success\t||\tRenderState shift_all shifts actions with parts\t", - "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", - "Success\t||\tRenderState shift_all invalidates index when content shifted\t", - "Success\t||\tRenderState shift_all exits early when content found before from_line\t", - "Success\t||\tRenderState update_part_data updates part reference\t", - "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", - "\t", - "Success: \t37\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t", - "Success\t||\tserver_job exposes expected public functions\t", - "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", - "Success\t||\tserver_job call_api rejects on non 2xx\t", - "Success\t||\tserver_job stream_api forwards chunks\t", - "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t", - "Success\t||\topencode.config uses default values when no options are provided\t", - "Success\t||\topencode.config merges user options with defaults\t", - "\t", - "Success: \t2\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t", - "Success\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t", - "Success\t||\topencode.ui.context_bar setup subscribes to state changes\t", - "\t", - "Success: \t9\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t", - "Success\t||\tapi_client should create a new client instance\t", - "Success\t||\tapi_client should remove trailing slash from base_url\t", - "Success\t||\tapi_client should create client using create factory function\t", - "Success\t||\tapi_client should have all expected API methods\t", - "Success\t||\tapi_client should construct URLs correctly with query parameters\t", - "\t", - "Success: \t5\t", - "Failed : \t0\t", - "========================================\t", - "Error detected while processing command line:", - "opencode command not found - please install and configure opencode before using this plugin", - "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t", - "Success\t||\topencode.core open creates windows if they don't exist\t", - "Success\t||\topencode.core open handles new session properly\t", - "Success\t||\topencode.core open focuses the appropriate window\t", - "Success\t||\topencode.core select_session filters sessions by description and parentID\t", - "Success\t||\topencode.core send_message sends a message via api_client\t", - "Success\t||\topencode.core send_message creates new session when none active\t", - "Success\t||\topencode.core send_message persist options in state when sending message\t", - "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", - "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", - "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", - "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", - "\t", - "Success: \t11\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t", - "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", - "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", - "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", - "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", - "Success\t||\topencode.session get_by_name returns the session with matching ID\t", - "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", - "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", - "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", - "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", - "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", - "Success\t||\topencode.session get_messages returns nil when session is nil\t", - "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", - "Success\t||\topencode.session get_messages returns messages with their parts\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t", - "Success\t||\topencode.state (observable) notifies listeners on key change\t", - "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", - "Success\t||\topencode.state (observable) can unregister listeners\t", - "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t", - "Success\t||\tEventManager should create a new instance\t", - "Success\t||\tEventManager should subscribe and emit events\t", - "Success\t||\tEventManager should handle multiple subscribers\t", - "Success\t||\tEventManager should unsubscribe correctly\t", - "Success\t||\tEventManager should track subscriber count\t", - "Success\t||\tEventManager should list event names\t", - "Success\t||\tEventManager should handle starting and stopping\t", - "Success\t||\tEventManager should not start multiple times\t", - "\t", - "Success: \t8\t", - "Failed : \t0\t", - "========================================\t", - "File not added to context. Could not read.", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t", - "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", - "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", - "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", - "Success\t||\textract_legacy_tag extracts content between tags\t", - "Success\t||\textract_legacy_tag returns nil if tag not found\t", - "Success\t||\tformat_message returns a parts array with prompt as first part\t", - "Success\t||\tformat_message includes mentioned_files and subagents\t", - "Success\t||\tdelta_context removes current_file if unchanged\t", - "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", - "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", - "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", - "\t", - "Success: \t13\t", - "Failed : \t0\t", - "========================================\t", - ":OpencodeOpenInput is deprecated. Use `:Opencode open input` instead", - ":OpencodeStop is deprecated. Use `:Opencode cancel` instead", - ":OpencodeClose is deprecated. Use `:Opencode close` instead", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t", - "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", - "Success\t||\topencode.api setup registers the main Opencode command and legacy commands\t", - "Success\t||\topencode.api setup sets up legacy command functions that route to main command\t", - "Success\t||\topencode.api Lua API provides callable functions that match commands\t", - "Success\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t", - "Success\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t", - "Success\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t", - "Success\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t", - "Success\t||\topencode.api run command argument parsing works with run_new command\t", - "Success\t||\topencode.api run command argument parsing requires a prompt after prefixes\t", - "Success\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t", - "\t", - "Success: \t11\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t", - "Success\t||\tutil.parse_dot_args parses flat booleans\t", - "Success\t||\tutil.parse_dot_args parses nested dot notation\t", - "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", - "Success\t||\tutil.parse_dot_args parses numbers\t", - "Success\t||\tutil.parse_dot_args handles empty string\t", - "Success\t||\tutil.parse_run_args parses no prefixes\t", - "Success\t||\tutil.parse_run_args parses single agent prefix\t", - "Success\t||\tutil.parse_run_args parses single model prefix\t", - "Success\t||\tutil.parse_run_args parses single context prefix\t", - "Success\t||\tutil.parse_run_args parses multiple prefixes in order\t", - "Success\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t", - "Success\t||\tutil.parse_run_args handles empty prompt after prefixes\t", - "Success\t||\tutil.parse_run_args handles empty string\t", - "Success\t||\tutil.parse_run_args stops parsing at first non-prefix token\t", - "\t", - "Success: \t14\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t", - "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", - "Success\t||\tsnapshot.restore notifies error if no active session\t", - "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", - "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", - "\t", - "Success: \t4\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t", - "Success\t||\tID module should generate ascending session IDs\t", - "Success\t||\tID module should generate descending message IDs\t", - "Success\t||\tID module should validate given IDs correctly\t", - "Success\t||\tID module should throw error for invalid given IDs\t", - "Success\t||\tID module should validate schemas correctly\t", - "Success\t||\tID module should return available prefixes\t", - "Success\t||\tID module should generate IDs with correct length structure\t", - "\t", - "Success: \t7\t", - "Failed : \t0\t", - "========================================\t", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t", - "Success\t||\tTimer Timer.new creates a new timer with required options\t", - "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", - "Success\t||\tTimer Timer.new stores optional parameters\t", - "Success\t||\tTimer Timer:start starts a repeating timer\t", - "Success\t||\tTimer Timer:start starts a one-shot timer\t", - "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", - "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", - "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", - "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", - "Success\t||\tTimer Timer:start throws error when timer creation fails\t", - "Success\t||\tTimer Timer:stop stops a running timer\t", - "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", - "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", - "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", - "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", - "Success\t||\tTimer Timer:is_running returns true when timer is running\t", - "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", - "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", - "Success\t||\tTimer Integration tests can restart a stopped timer\t", - "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", - "\t", - "Success: \t20\t", - "Failed : \t0\t", - "========================================\t", - "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", - "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", - "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", - "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", - "", - "========================================\t", - "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t", - "Success\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays ansi-codes correctly (session)\t", - "Success\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays api-abort correctly (session)\t", - "Success\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays api-error correctly (session)\t", - "Success\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays cursor_data correctly (session)\t", - "Success\t||\trenderer replays diff correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays diff correctly (session)\t", - "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays markdown-codefence correctly (session)\t", - "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", - "Success\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays perf correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays perf correctly (session)\t", - "Success\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays permission-denied correctly (session)\t", - "Success\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays permission correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays permission correctly (session)\t", - "Success\t||\trenderer replays planning correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays planning correctly (session)\t", - "Success\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays redo-all correctly (session)\t", - "Success\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays redo-once correctly (session)\t", - "Success\t||\trenderer replays revert correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays revert correctly (session)\t", - "Success\t||\trenderer replays selection correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays selection correctly (session)\t", - "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays simple-session correctly (session)\t", - "Success\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays tool-invalid correctly (session)\t", - "Success\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t", - "Success\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t", - "Success\t||\trenderer replays updating-text correctly (session)\t", - "\t", - "Success: \t60\t", - "Failed : \t0\t", - "========================================\t", - "✓ Unit tests passed", - "------------------------------------------------", - "All tests passed successfully!", - "", - "`````", - "", - "----", - "", - "", - "All steps are complete and all tests pass! Here’s a summary of what was fixed:", - "", - "- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", - "- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.", - "- ✅ Verified the fixes by running the full test suite—no errors or failures.", - "", - "Your file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!", - "", - "" + "actions": [ + { + "args": [ + "f33f38a70b284207e092c2c578a24e96fdd1bd4d" + ], + "display_line": 94, + "key": "R", + "range": { + "from": 94, + "to": 94 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "f33f38a70b284207e092c2c578a24e96fdd1bd4d" + ], + "display_line": 94, + "key": "A", + "range": { + "from": 94, + "to": 94 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "f33f38a70b284207e092c2c578a24e96fdd1bd4d" + ], + "display_line": 94, + "key": "D", + "range": { + "from": 94, + "to": 94 + }, + "text": "[D]iff", + "type": "diff_open" + }, + { + "args": [ + "8e7903714919009004aad8754db0035fb47ecb24" + ], + "display_line": 57, + "key": "R", + "range": { + "from": 57, + "to": 57 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "8e7903714919009004aad8754db0035fb47ecb24" + ], + "display_line": 57, + "key": "A", + "range": { + "from": 57, + "to": 57 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "8e7903714919009004aad8754db0035fb47ecb24" + ], + "display_line": 57, + "key": "D", + "range": { + "from": 57, + "to": 57 + }, + "text": "[D]iff", + "type": "diff_open" + } ], "extmarks": [ [ @@ -596,20 +86,37 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-11-03 13:34:57)", "OpencodeHint"], - [" [msg_a49ed91d6001coTsjFq9x6FF5W]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-11-03 13:34:57)", + "OpencodeHint" + ], + [ + " [msg_a49ed91d6001coTsjFq9x6FF5W]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -618,13 +125,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -633,13 +145,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -648,13 +165,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -663,13 +185,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -678,13 +205,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -693,13 +225,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -708,13 +245,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -723,20 +265,37 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-03 13:34:59)", "OpencodeHint"], - [" [msg_a49ed9828001mN6CDcWPnGnpHS]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-03 13:34:59)", + "OpencodeHint" + ], + [ + " [msg_a49ed9828001mN6CDcWPnGnpHS]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -745,13 +304,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -760,13 +324,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -775,13 +344,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -790,13 +364,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -805,13 +384,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -820,13 +404,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -835,13 +424,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -849,18 +443,23 @@ 46, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 47, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffAdd" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -869,13 +468,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -883,18 +487,23 @@ 47, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 48, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffAdd" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -903,13 +512,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -917,18 +531,23 @@ 48, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 49, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffAdd" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -937,13 +556,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -951,18 +575,23 @@ 49, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 50, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffAdd" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -971,13 +600,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -986,13 +620,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1001,13 +640,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1016,13 +660,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1031,13 +680,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1046,13 +700,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1061,13 +720,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1076,20 +740,37 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-03 13:35:06)", "OpencodeHint"], - [" [msg_a49edb694001syExFxMUW1ik3n]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-03 13:35:06)", + "OpencodeHint" + ], + [ + " [msg_a49edb694001syExFxMUW1ik3n]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -1098,13 +779,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1113,13 +799,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1128,13 +819,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1143,13 +839,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1158,13 +859,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1173,13 +879,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1188,13 +899,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1202,18 +918,23 @@ 85, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 86, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["-", "OpencodeDiffDelete"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffDelete" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -1222,13 +943,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1236,18 +962,23 @@ 86, 0, { - "ns_id": 3, "end_col": 0, + "end_right_gravity": false, "end_row": 87, "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "right_gravity": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "hl_group": "OpencodeDiffAdd" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -1256,13 +987,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1271,13 +1007,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1286,13 +1027,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1301,13 +1047,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1316,13 +1067,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1331,13 +1087,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1346,13 +1107,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1361,20 +1127,37 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-03 13:35:10)", "OpencodeHint"], - [" [msg_a49edc4640017DZPFVUPHCm7Ji]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-03 13:35:10)", + "OpencodeHint" + ], + [ + " [msg_a49edc4640017DZPFVUPHCm7Ji]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ], [ @@ -1383,13 +1166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1398,13 +1186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1413,13 +1206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1428,13 +1226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1443,13 +1246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1458,13 +1266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1473,13 +1286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1488,13 +1306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1503,13 +1326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1518,13 +1346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1533,13 +1366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1548,13 +1386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1563,13 +1406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1578,13 +1426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1593,13 +1446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1608,13 +1466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1623,13 +1486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1638,13 +1506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1653,13 +1526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1668,13 +1546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1683,13 +1566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1698,13 +1586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1713,13 +1606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1728,13 +1626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1743,13 +1646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1758,13 +1666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1773,13 +1686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1788,13 +1706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1803,13 +1726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1818,13 +1746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1833,13 +1766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1848,13 +1786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1863,13 +1806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1878,13 +1826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1893,13 +1846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1908,13 +1866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1923,13 +1886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1938,13 +1906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1953,13 +1926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1968,13 +1946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1983,13 +1966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -1998,13 +1986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2013,13 +2006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2028,13 +2026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2043,13 +2046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2058,13 +2066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2073,13 +2086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2088,13 +2106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2103,13 +2126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2118,13 +2146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2133,13 +2166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2148,13 +2186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2163,13 +2206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2178,13 +2226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2193,13 +2246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2208,13 +2266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2223,13 +2286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2238,13 +2306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2253,13 +2326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2268,13 +2346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2283,13 +2366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2298,13 +2386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2313,13 +2406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2328,13 +2426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2343,13 +2446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2358,13 +2466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2373,13 +2486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2388,13 +2506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2403,13 +2526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2418,13 +2546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2433,13 +2566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2448,13 +2586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2463,13 +2606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2478,13 +2626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2493,13 +2646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2508,13 +2666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2523,13 +2686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2538,13 +2706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2553,13 +2726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2568,13 +2746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2583,13 +2766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2598,13 +2786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2613,13 +2806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2628,13 +2826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2643,13 +2846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2658,13 +2866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2673,13 +2886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2688,13 +2906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2703,13 +2926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2718,13 +2946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2733,13 +2966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2748,13 +2986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2763,13 +3006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2778,13 +3026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2793,13 +3046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2808,13 +3066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2823,13 +3086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2838,13 +3106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2853,13 +3126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2868,13 +3146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2883,13 +3166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2898,13 +3186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2913,13 +3206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2928,13 +3226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2943,13 +3246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2958,13 +3266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2973,13 +3286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -2988,13 +3306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3003,13 +3326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3018,13 +3346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3033,13 +3366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3048,13 +3386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3063,13 +3406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3078,13 +3426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3093,13 +3446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3108,13 +3466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3123,13 +3486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3138,13 +3506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3153,13 +3526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3168,13 +3546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "right_gravity": true, "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -3183,13 +3566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3198,13 +3586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3213,13 +3606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3228,13 +3626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3243,13 +3646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3258,13 +3666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3273,13 +3686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3288,13 +3706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3303,13 +3726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3318,13 +3746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3333,13 +3766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3348,13 +3786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3363,13 +3806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3378,13 +3826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3393,13 +3846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3408,13 +3866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3423,13 +3886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3438,13 +3906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3453,13 +3926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3468,13 +3946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3483,13 +3966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3498,13 +3986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3513,13 +4006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3528,13 +4026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3543,13 +4046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3558,13 +4066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3573,13 +4086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3588,13 +4106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3603,13 +4126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3618,13 +4146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3633,13 +4166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3648,13 +4186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3663,13 +4206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3678,13 +4226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3693,13 +4246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3708,13 +4266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3723,13 +4286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3738,13 +4306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3753,13 +4326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3768,13 +4346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3783,13 +4366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3798,13 +4386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3813,13 +4406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3828,13 +4426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3843,13 +4446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3858,13 +4466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3873,13 +4486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3888,13 +4506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3903,13 +4526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3918,13 +4546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3933,13 +4566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3948,13 +4586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3963,13 +4606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3978,13 +4626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -3993,13 +4646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4008,13 +4666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4023,13 +4686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4038,13 +4706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4053,13 +4726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4068,13 +4746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4083,13 +4766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4098,13 +4786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4113,13 +4806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4128,13 +4826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4143,13 +4846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4158,13 +4866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4173,13 +4886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4188,13 +4906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4203,13 +4926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4218,13 +4946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4233,13 +4966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4248,13 +4986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4263,13 +5006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4278,13 +5026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4293,13 +5046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4308,13 +5066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4323,13 +5086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4338,13 +5106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4353,13 +5126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4368,13 +5146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4383,13 +5166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4398,13 +5186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4413,13 +5206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4428,13 +5226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4443,13 +5246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4458,13 +5266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4473,13 +5286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4488,13 +5306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4503,13 +5326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4518,13 +5346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4533,13 +5366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4548,13 +5386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4563,13 +5406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4578,13 +5426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4593,13 +5446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4608,13 +5466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4623,13 +5486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4638,13 +5506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4653,13 +5526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4668,13 +5546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4683,13 +5566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4698,13 +5586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4713,13 +5606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4728,13 +5626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4743,13 +5646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4758,13 +5666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4773,13 +5686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4788,13 +5706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4803,13 +5726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4818,13 +5746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4833,13 +5766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4848,13 +5786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4863,13 +5806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4878,13 +5826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4893,13 +5846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4908,13 +5866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4923,13 +5886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4938,13 +5906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4953,13 +5926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4968,13 +5946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4983,13 +5966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -4998,13 +5986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5013,13 +6006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5028,13 +6026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5043,13 +6046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5058,13 +6066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5073,13 +6086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5088,13 +6106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5103,13 +6126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5118,13 +6146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5133,13 +6166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5148,13 +6186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5163,13 +6206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5178,13 +6226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5193,13 +6246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5208,13 +6266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5223,13 +6286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5238,13 +6306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5253,13 +6326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5268,13 +6346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5283,13 +6366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5298,13 +6386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5313,13 +6406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5328,13 +6426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5343,13 +6446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5358,13 +6466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "right_gravity": true, "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -5373,13 +6486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5388,13 +6506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5403,13 +6526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5418,13 +6546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5433,13 +6566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5448,13 +6586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5463,13 +6606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5478,13 +6626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5493,13 +6646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5508,13 +6666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5523,13 +6686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5538,13 +6706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5553,13 +6726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5568,13 +6746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5583,13 +6766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5598,13 +6786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5613,13 +6806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5628,13 +6826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5643,13 +6846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5658,13 +6866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5673,13 +6886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5688,13 +6906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5703,13 +6926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5718,13 +6946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5733,13 +6966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5748,13 +6986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5763,13 +7006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5778,13 +7026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5793,13 +7046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5808,13 +7066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5823,13 +7086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5838,13 +7106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5853,13 +7126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5868,13 +7146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5883,13 +7166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5898,13 +7186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5913,13 +7206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5928,13 +7226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5943,13 +7246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5958,13 +7266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5973,13 +7286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -5988,13 +7306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6003,13 +7326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6018,13 +7346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6033,13 +7366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6048,13 +7386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6063,13 +7406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6078,13 +7426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6093,13 +7446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6108,13 +7466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6123,13 +7486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6138,13 +7506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6153,13 +7526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6168,13 +7546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6183,13 +7566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6198,13 +7586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6213,13 +7606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6228,13 +7626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6243,13 +7646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6258,13 +7666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6273,13 +7686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6288,13 +7706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6303,13 +7726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6318,13 +7746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6333,13 +7766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6348,13 +7786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6363,13 +7806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6378,13 +7826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6393,13 +7846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6408,13 +7866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6423,13 +7886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6438,13 +7906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6453,13 +7926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6468,13 +7946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6483,13 +7966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6498,13 +7986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6513,13 +8006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6528,13 +8026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6543,13 +8046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6558,13 +8066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6573,13 +8086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6588,13 +8106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6603,13 +8126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6618,13 +8146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6633,13 +8166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6648,13 +8186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6663,13 +8206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6678,13 +8226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6693,13 +8246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6708,13 +8266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6723,13 +8286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6738,13 +8306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6753,13 +8326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6768,13 +8346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6783,13 +8366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6798,13 +8386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6813,13 +8406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6828,13 +8426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6843,13 +8446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6858,13 +8466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6873,13 +8486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6888,13 +8506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6903,13 +8526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6918,13 +8546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6933,13 +8566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6948,13 +8586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6963,13 +8606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6978,13 +8626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -6993,13 +8646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7008,13 +8666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7023,13 +8686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7038,13 +8706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7053,13 +8726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7068,13 +8746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7083,13 +8766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7098,13 +8786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7113,13 +8806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7128,13 +8826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7143,13 +8846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7158,13 +8866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7173,13 +8886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7188,13 +8906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7203,13 +8926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7218,13 +8946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7233,13 +8966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7248,13 +8986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7263,13 +9006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7278,13 +9026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7293,13 +9046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7308,13 +9066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7323,13 +9086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7338,13 +9106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7353,13 +9126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7368,13 +9146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7383,13 +9166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7398,13 +9186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7413,13 +9206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7428,13 +9226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7443,13 +9246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7458,13 +9266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7473,13 +9286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7488,13 +9306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7503,13 +9326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7518,13 +9346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7533,13 +9366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7548,13 +9386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7563,13 +9406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7578,13 +9426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7593,13 +9446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7608,13 +9466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7623,13 +9486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7638,13 +9506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7653,13 +9526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7668,13 +9546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7683,13 +9566,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7698,13 +9586,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7713,13 +9606,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7728,13 +9626,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7743,13 +9646,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7758,13 +9666,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7773,13 +9686,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7788,13 +9706,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7803,13 +9726,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7818,13 +9746,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7833,13 +9766,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7848,13 +9786,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7863,13 +9806,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7878,13 +9826,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7893,13 +9846,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7908,13 +9866,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7923,13 +9886,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7938,13 +9906,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7953,13 +9926,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7968,13 +9946,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7983,13 +9966,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -7998,13 +9986,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8013,13 +10006,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8028,13 +10026,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8043,13 +10046,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8058,13 +10066,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8073,13 +10086,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8088,13 +10106,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8103,13 +10126,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8118,13 +10146,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8133,13 +10166,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8148,13 +10186,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8163,13 +10206,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8178,13 +10226,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8193,13 +10246,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8208,13 +10266,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8223,13 +10286,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8238,13 +10306,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8253,13 +10326,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8268,13 +10346,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8283,13 +10366,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8298,13 +10386,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8313,13 +10406,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8328,13 +10426,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8343,13 +10446,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8358,13 +10466,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8373,13 +10486,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8388,13 +10506,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8403,13 +10526,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8418,13 +10546,18 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -1, - "virt_text_hide": false + "virt_text_win_col": -1 } ], [ @@ -8433,72 +10566,629 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-03 13:35:15)", "OpencodeHint"], - [" [msg_a49edd87f001eOJDDoq5Vb5hra]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-03 13:35:15)", + "OpencodeHint" + ], + [ + " [msg_a49edd87f001eOJDDoq5Vb5hra]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_hide": false + "virt_text_win_col": -3 } ] ], - "actions": [ - { - "display_line": 94, - "text": "[R]evert file", - "range": { "to": 94, "from": 94 }, - "type": "diff_revert_selected_file", - "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], - "key": "R" - }, - { - "display_line": 94, - "text": "Revert [A]ll", - "range": { "to": 94, "from": 94 }, - "type": "diff_revert_all", - "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], - "key": "A" - }, - { - "display_line": 94, - "text": "[D]iff", - "range": { "to": 94, "from": 94 }, - "type": "diff_open", - "args": ["f33f38a70b284207e092c2c578a24e96fdd1bd4d"], - "key": "D" - }, - { - "display_line": 57, - "text": "[R]evert file", - "range": { "to": 57, "from": 57 }, - "type": "diff_revert_selected_file", - "args": ["8e7903714919009004aad8754db0035fb47ecb24"], - "key": "R" - }, - { - "display_line": 57, - "text": "Revert [A]ll", - "range": { "to": 57, "from": 57 }, - "type": "diff_revert_all", - "args": ["8e7903714919009004aad8754db0035fb47ecb24"], - "key": "A" - }, - { - "display_line": 57, - "text": "[D]iff", - "range": { "to": 57, "from": 57 }, - "type": "diff_open", - "args": ["8e7903714919009004aad8754db0035fb47ecb24"], - "key": "D" - } + "lines": [ + "----", + "", + "", + "help me fix the file", + "", + "[`lua/opencode/core.lua`](lua/opencode/core.lua)", + "", + "**Diagnostics:**  (3)", + "", + "----", + "", + "", + "Let's address the three EmmyLua diagnostics at line 130 in  `lua/opencode/core.lua`:", + "", + "### 1. `param-type-not-match`: expected `string` but found `string?`", + "- **Location:** `state.active_session.id` (line 130, col 20)", + "- **Cause:** `state.active_session` may be `nil`, so `state.active_session.id` could error or be `nil`. The function expects a `string`, not a nullable string.", + "- **Fix:** Add a nil check for `state.active_session` before accessing `.id`.", + "", + "### 2. `param-type-not-match`: expected `{ agent: string?, messageID: string?, model: {...}?, parts: Part[], system: string?, tools: table<...>? }` but found `{ model = table, agent = any, parts = OpencodeMessagePart[], ... }`", + "- **Location:** `params` argument to `:create_message` (line 130, col 45)", + "- **Cause:** The `model` field in `params` is `{ providerID = string?, modelID = string? }`, but the expected type is `{ modelID: string, providerID: string }?`. The order and possibly the optionality are mismatched.", + "- **Fix:** Ensure the `model` field matches the expected type exactly: `{ modelID: string, providerID: string }`.", + "", + "### 3. `need-check-nil`: `state.active_session may be nil`", + "- **Location:** Same as #1. This is a reminder to check for nil before accessing `state.active_session.id`.", + "", + "---", + "", + "## Step-by-step Fix Plan", + "", + "```markdown", + "- [ ] 1. Add a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", + "- [ ] 2. Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", + "- [ ] 3. Test the changes to ensure the diagnostics are resolved and the function works as expected.", + "```", + "", + "Let's start with step 1: Add a nil check for `state.active_session` before accessing `.id`.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", + "", + "`````lua", + " ", + " params.parts = context.format_message(prompt, opts.context)", + " M.before_run(opts)", + " ", + " if not state.active_session or not state.active_session.id then", + " vim.notify('No active session to send message to', vim.log.levels.ERROR)", + " return", + " end", + " state.api_client", + " :create_message(state.active_session.id, params)", + " :and_then(function(response)", + " if not response or not response.info or not response.parts then", + "", + "`````", + "", + "**󰻛 Created Snapshot** `8e790371`", + "", + "----", + "", + "", + "Step 1 is complete: nil check for `state.active_session` before accessing `.id` is added.", + "", + "Now, let's address step 2: Ensure the `model` field in `params` matches the expected type `{ modelID: string, providerID: string }`.", + "", + "Currently, the code is:", + "```lua", + "if opts.model then", + " local provider, model = opts.model:match('^(.-)/(.+)$')", + " params.model = { providerID = provider, modelID = model }", + " state.current_model = opts.model", + "end", + "```", + "The expected type is `{ modelID: string, providerID: string }`, but EmmyLua expects the order to be `modelID` first, then `providerID`. Let's update the assignment to match the expected order.", + "", + "I'll fix this now.", + "", + "** edit** `/home/francis/Projects/_nvim/opencode.nvim/lua/opencode/core.lua`", + "", + "`````lua", + " local params = {}", + " ", + " if opts.model then", + " local provider, model = opts.model:match('^(.-)/(.+)$')", + " params.model = { providerID = provider, modelID = model }", + " params.model = { modelID = model, providerID = provider }", + " state.current_model = opts.model", + " end", + " ", + " if opts.agent then", + "", + "`````", + "", + "**󰻛 Created Snapshot** `f33f38a7`", + "", + "----", + "", + "", + "Step 2 is complete: the `model` field in `params` now matches the expected type `{ modelID = model, providerID = provider }`.", + "", + "Now, let's move to step 3: Test the changes to ensure the diagnostics are resolved and the function works as expected.", + "", + "I'll run the test suite using the provided script to verify everything is working.", + "", + "** run** `Run all tests to verify code changes`", + "", + "`````bash", + "> ./run_tests.sh", + "", + "Running tests for opencode.nvim", + "------------------------------------------------", + "Starting...Scheduling: ./tests/minimal/plugin_spec.lua", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/minimal/plugin_spec.lua\t", + "Success\t||\topencode.nvim plugin loads the plugin without errors\t", + "Success\t||\topencode.nvim plugin can be set up with custom config\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "✓ Minimal tests passed", + "------------------------------------------------", + "Starting...Scheduling: ./tests/unit/keymap_spec.lua", + "Scheduling: ./tests/unit/opencode_server_spec.lua", + "Scheduling: ./tests/unit/context_completion_spec.lua", + "Scheduling: ./tests/unit/config_file_spec.lua", + "Scheduling: ./tests/unit/init_spec.lua", + "Scheduling: ./tests/unit/state_spec.lua", + "Scheduling: ./tests/unit/render_state_spec.lua", + "Scheduling: ./tests/unit/server_job_spec.lua", + "Scheduling: ./tests/unit/config_spec.lua", + "Scheduling: ./tests/unit/context_bar_spec.lua", + "Scheduling: ./tests/unit/renderer_spec.lua", + "Scheduling: ./tests/unit/api_client_spec.lua", + "Scheduling: ./tests/unit/core_spec.lua", + "Scheduling: ./tests/unit/session_spec.lua", + "Scheduling: ./tests/unit/event_manager_spec.lua", + "Scheduling: ./tests/unit/api_spec.lua", + "Scheduling: ./tests/unit/context_spec.lua", + "Scheduling: ./tests/unit/timer_spec.lua", + "Scheduling: ./tests/unit/util_spec.lua", + "Scheduling: ./tests/unit/snapshot_spec.lua", + "Scheduling: ./tests/unit/id_spec.lua", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_completion_spec.lua\t", + "Success\t||\tcontext completion get_source should return a completion source\t", + "Success\t||\tcontext completion complete function should return empty table when trigger char does not match\t", + "Success\t||\tcontext completion complete function should return context items when trigger char matches\t", + "Success\t||\tcontext completion complete function should include selection items when selections exist\t", + "Success\t||\tcontext completion complete function should include mentioned files when they exist\t", + "Success\t||\tcontext completion complete function should include subagent items when they exist\t", + "Success\t||\tcontext completion complete function should filter items based on input\t", + "Success\t||\tcontext completion complete function should sort items with available items first\t", + "Success\t||\tcontext completion on_complete function should handle nil item gracefully\t", + "Success\t||\tcontext completion on_complete function should handle item without data gracefully\t", + "Success\t||\tcontext completion on_complete function should toggle context enabled state for toggleable items\t", + "Success\t||\tcontext completion on_complete function should remove mentioned file when selected\t", + "Success\t||\tcontext completion on_complete function should remove subagent when selected\t", + "Success\t||\tcontext completion on_complete function should remove selection when selection item selected\t", + "Success\t||\tcontext completion on_complete function should clean up trigger character from current line\t", + "Success\t||\tcontext completion formatting functions should format diagnostics correctly\t", + "Success\t||\tcontext completion formatting functions should format cursor data correctly\t", + "Success\t||\tcontext completion formatting functions should format selection correctly\t", + "Success\t||\tcontext completion edge cases should handle empty context gracefully\t", + "Success\t||\tcontext completion edge cases should handle disabled contexts correctly\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/keymap_spec.lua\t", + "Success\t||\topencode.keymap setup sets up keymap with new format configured keys\t", + "Success\t||\topencode.keymap setup sets up keymap with old format configured keys (normalized)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (new format)\t", + "Success\t||\topencode.keymap setup sets up callbacks that execute the correct commands (old format normalized)\t", + "Success\t||\topencode.keymap normalize_keymap normalizes old format keymap to new format correctly\t", + "Success\t||\topencode.keymap normalize_keymap shows error message for unknown API functions\t", + "Success\t||\topencode.keymap normalize_keymap uses custom description from config_entry\t", + "Success\t||\topencode.keymap normalize_keymap falls back to API description when no custom desc provided\t", + "Success\t||\topencode.keymap setup_window_keymaps handles unknown API functions with error message\t", + "Success\t||\topencode.keymap setup_window_keymaps uses custom description for window keymaps\t", + "Success\t||\topencode.keymap setup_permission_keymap sets up permission keymaps when there is a current permission\t", + "Success\t||\topencode.keymap setup_permission_keymap should delete existing permission keymaps if no current permission exists after being set\t", + "Success\t||\topencode.keymap setup_permission_keymap does not set permission keymaps when there is no current permission\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/opencode_server_spec.lua\t", + "Success\t||\topencode.opencode_server creates a new server object\t", + "Success\t||\topencode.opencode_server spawn promise resolves when stdout emits server URL\t", + "Success\t||\topencode.opencode_server shutdown resolves shutdown_promise and clears fields\t", + "Success\t||\topencode.opencode_server calls on_error when stderr is triggered\t", + "Success\t||\topencode.opencode_server calls on_exit and clears fields when process exits\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_file_spec.lua\t", + "Success\t||\tconfig_file.setup lazily loads config when accessed\t", + "Success\t||\tconfig_file.setup get_opencode_agents returns primary + defaults\t", + "Success\t||\tconfig_file.setup get_opencode_agents respects disabled defaults\t", + "Success\t||\tconfig_file.setup get_opencode_project returns project\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/init_spec.lua\t", + "Success\t||\topencode has setup function in the public API\t", + "Success\t||\topencode main module can be required without errors\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/render_state_spec.lua\t", + "Success\t||\tRenderState new and reset creates a new instance\t", + "Success\t||\tRenderState new and reset resets to empty state\t", + "Success\t||\tRenderState set_message sets a new message\t", + "Success\t||\tRenderState set_message updates line index for message\t", + "Success\t||\tRenderState set_message updates existing message\t", + "Success\t||\tRenderState set_part sets a new part\t", + "Success\t||\tRenderState set_part updates line index for part\t", + "Success\t||\tRenderState set_part initializes actions array\t", + "Success\t||\tRenderState get_part_at_line returns part at line\t", + "Success\t||\tRenderState get_part_at_line returns nil for line without part\t", + "Success\t||\tRenderState get_message_at_line returns message at line\t", + "Success\t||\tRenderState get_message_at_line returns nil for line without message\t", + "Success\t||\tRenderState get_part_by_call_id finds part by call ID\t", + "Success\t||\tRenderState get_part_by_call_id returns nil when call ID not found\t", + "Success\t||\tRenderState actions adds actions to part\t", + "Success\t||\tRenderState actions adds actions with offset\t", + "Success\t||\tRenderState actions clears actions for part\t", + "Success\t||\tRenderState actions gets actions at line\t", + "Success\t||\tRenderState actions gets all actions from all parts\t", + "Success\t||\tRenderState update_part_lines updates part line positions\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when expanding\t", + "Success\t||\tRenderState update_part_lines shifts subsequent content when shrinking\t", + "Success\t||\tRenderState update_part_lines returns false for non-existent part\t", + "Success\t||\tRenderState remove_part removes part and shifts subsequent content\t", + "Success\t||\tRenderState remove_part clears line index for removed part\t", + "Success\t||\tRenderState remove_part returns false for non-existent part\t", + "Success\t||\tRenderState remove_message removes message and shifts subsequent content\t", + "Success\t||\tRenderState remove_message clears line index for removed message\t", + "Success\t||\tRenderState remove_message returns false for non-existent message\t", + "Success\t||\tRenderState shift_all does nothing when delta is 0\t", + "Success\t||\tRenderState shift_all shifts content at or after from_line\t", + "Success\t||\tRenderState shift_all shifts actions with parts\t", + "Success\t||\tRenderState shift_all does not rebuild index when nothing shifted\t", + "Success\t||\tRenderState shift_all invalidates index when content shifted\t", + "Success\t||\tRenderState shift_all exits early when content found before from_line\t", + "Success\t||\tRenderState update_part_data updates part reference\t", + "Success\t||\tRenderState update_part_data does nothing for non-existent part\t", + "\t", + "Success: \t37\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/server_job_spec.lua\t", + "Success\t||\tserver_job exposes expected public functions\t", + "Success\t||\tserver_job call_api resolves with decoded json and toggles is_job_running\t", + "Success\t||\tserver_job call_api rejects on non 2xx\t", + "Success\t||\tserver_job stream_api forwards chunks\t", + "Success\t||\tserver_job ensure_server spawns a new opencode server only once\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/config_spec.lua\t", + "Success\t||\topencode.config uses default values when no options are provided\t", + "Success\t||\topencode.config merges user options with defaults\t", + "\t", + "Success: \t2\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_bar_spec.lua\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders minimal winbar with right aligh token only\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with current file when present\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with multiple context elements\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar renders winbar with diagnostics\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar respects context enabled settings\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar handles empty winbar gracefully\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar does nothing when window is invalid\t", + "Success\t||\topencode.ui.context_bar opencode.ui.context_bar uses provided windows parameter\t", + "Success\t||\topencode.ui.context_bar setup subscribes to state changes\t", + "\t", + "Success: \t9\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_client_spec.lua\t", + "Success\t||\tapi_client should create a new client instance\t", + "Success\t||\tapi_client should remove trailing slash from base_url\t", + "Success\t||\tapi_client should create client using create factory function\t", + "Success\t||\tapi_client should have all expected API methods\t", + "Success\t||\tapi_client should construct URLs correctly with query parameters\t", + "\t", + "Success: \t5\t", + "Failed : \t0\t", + "========================================\t", + "Error detected while processing command line:", + "opencode command not found - please install and configure opencode before using this plugin", + "Unsupported opencode CLI version: opencode 0.4.1. Requires >= 0.4.2", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/core_spec.lua\t", + "Success\t||\topencode.core open creates windows if they don't exist\t", + "Success\t||\topencode.core open handles new session properly\t", + "Success\t||\topencode.core open focuses the appropriate window\t", + "Success\t||\topencode.core select_session filters sessions by description and parentID\t", + "Success\t||\topencode.core send_message sends a message via api_client\t", + "Success\t||\topencode.core send_message creates new session when none active\t", + "Success\t||\topencode.core send_message persist options in state when sending message\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when opencode executable is missing\t", + "Success\t||\topencode.core opencode_ok (version checks) returns false when version is below required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version equals required\t", + "Success\t||\topencode.core opencode_ok (version checks) returns true when version is above required\t", + "\t", + "Success: \t11\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/session_spec.lua\t", + "Success\t||\topencode.session get_last_workspace_session returns the most recent session for current workspace\t", + "Success\t||\topencode.session get_last_workspace_session returns nil when no sessions match the workspace\t", + "Success\t||\topencode.session get_last_workspace_session handles JSON parsing errors\t", + "Success\t||\topencode.session get_last_workspace_session handles empty session list\t", + "Success\t||\topencode.session get_by_name returns the session with matching ID\t", + "Success\t||\topencode.session get_by_name returns nil when no session matches the ID\t", + "Success\t||\topencode.session read_json_dir returns nil for non-existent directory\t", + "Success\t||\topencode.session read_json_dir returns nil when directory exists but has no JSON files\t", + "Success\t||\topencode.session read_json_dir returns decoded JSON content from directory\t", + "Success\t||\topencode.session read_json_dir skips invalid JSON files\t", + "Success\t||\topencode.session get_messages returns nil when session is nil\t", + "Success\t||\topencode.session get_messages returns nil when messages directory does not exist\t", + "Success\t||\topencode.session get_messages returns messages with their parts\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/state_spec.lua\t", + "Success\t||\topencode.state (observable) notifies listeners on key change\t", + "Success\t||\topencode.state (observable) notifies wildcard listeners on any key change\t", + "Success\t||\topencode.state (observable) can unregister listeners\t", + "Success\t||\topencode.state (observable) does not notify if value is unchanged\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/event_manager_spec.lua\t", + "Success\t||\tEventManager should create a new instance\t", + "Success\t||\tEventManager should subscribe and emit events\t", + "Success\t||\tEventManager should handle multiple subscribers\t", + "Success\t||\tEventManager should unsubscribe correctly\t", + "Success\t||\tEventManager should track subscriber count\t", + "Success\t||\tEventManager should list event names\t", + "Success\t||\tEventManager should handle starting and stopping\t", + "Success\t||\tEventManager should not start multiple times\t", + "\t", + "Success: \t8\t", + "Failed : \t0\t", + "========================================\t", + "File not added to context. Could not read.", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/context_spec.lua\t", + "Success\t||\textract_from_opencode_message extracts prompt, selected_text, and current_file from tags in parts\t", + "Success\t||\textract_from_opencode_message returns nils if message or parts missing\t", + "Success\t||\textract_from_message_legacy extracts legacy tags from text\t", + "Success\t||\textract_legacy_tag extracts content between tags\t", + "Success\t||\textract_legacy_tag returns nil if tag not found\t", + "Success\t||\tformat_message returns a parts array with prompt as first part\t", + "Success\t||\tformat_message includes mentioned_files and subagents\t", + "Success\t||\tdelta_context removes current_file if unchanged\t", + "Success\t||\tdelta_context removes mentioned_subagents if unchanged\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a file if filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent does not add file if not filereadable\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a selection\t", + "Success\t||\tadd_file/add_selection/add_subagent adds a subagent\t", + "\t", + "Success: \t13\t", + "Failed : \t0\t", + "========================================\t", + ":OpencodeOpenInput is deprecated. Use `:Opencode open input` instead", + ":OpencodeStop is deprecated. Use `:Opencode cancel` instead", + ":OpencodeClose is deprecated. Use `:Opencode close` instead", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/api_spec.lua\t", + "Success\t||\topencode.api commands table contains the expected commands with proper structure\t", + "Success\t||\topencode.api setup registers the main Opencode command and legacy commands\t", + "Success\t||\topencode.api setup sets up legacy command functions that route to main command\t", + "Success\t||\topencode.api Lua API provides callable functions that match commands\t", + "Success\t||\topencode.api run command argument parsing parses agent prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses model prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses context prefix and passes to send_message\t", + "Success\t||\topencode.api run command argument parsing parses multiple prefixes and passes all to send_message\t", + "Success\t||\topencode.api run command argument parsing works with run_new command\t", + "Success\t||\topencode.api run command argument parsing requires a prompt after prefixes\t", + "Success\t||\topencode.api run command argument parsing Lua API accepts opts directly without parsing\t", + "\t", + "Success: \t11\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/util_spec.lua\t", + "Success\t||\tutil.parse_dot_args parses flat booleans\t", + "Success\t||\tutil.parse_dot_args parses nested dot notation\t", + "Success\t||\tutil.parse_dot_args parses mixed nesting and booleans\t", + "Success\t||\tutil.parse_dot_args parses numbers\t", + "Success\t||\tutil.parse_dot_args handles empty string\t", + "Success\t||\tutil.parse_run_args parses no prefixes\t", + "Success\t||\tutil.parse_run_args parses single agent prefix\t", + "Success\t||\tutil.parse_run_args parses single model prefix\t", + "Success\t||\tutil.parse_run_args parses single context prefix\t", + "Success\t||\tutil.parse_run_args parses multiple prefixes in order\t", + "Success\t||\tutil.parse_run_args parses context with multiple comma-delimited values\t", + "Success\t||\tutil.parse_run_args handles empty prompt after prefixes\t", + "Success\t||\tutil.parse_run_args handles empty string\t", + "Success\t||\tutil.parse_run_args stops parsing at first non-prefix token\t", + "\t", + "Success: \t14\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/snapshot_spec.lua\t", + "Success\t||\tsnapshot.restore runs read-tree and checkout-index and notifies on success\t", + "Success\t||\tsnapshot.restore notifies error if no active session\t", + "Success\t||\tsnapshot.restore notifies error if read-tree fails\t", + "Success\t||\tsnapshot.restore notifies error if checkout-index fails\t", + "\t", + "Success: \t4\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/id_spec.lua\t", + "Success\t||\tID module should generate ascending session IDs\t", + "Success\t||\tID module should generate descending message IDs\t", + "Success\t||\tID module should validate given IDs correctly\t", + "Success\t||\tID module should throw error for invalid given IDs\t", + "Success\t||\tID module should validate schemas correctly\t", + "Success\t||\tID module should return available prefixes\t", + "Success\t||\tID module should generate IDs with correct length structure\t", + "\t", + "Success: \t7\t", + "Failed : \t0\t", + "========================================\t", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/timer_spec.lua\t", + "Success\t||\tTimer Timer.new creates a new timer with required options\t", + "Success\t||\tTimer Timer.new sets repeat_timer to false when explicitly disabled\t", + "Success\t||\tTimer Timer.new stores optional parameters\t", + "Success\t||\tTimer Timer:start starts a repeating timer\t", + "Success\t||\tTimer Timer:start starts a one-shot timer\t", + "Success\t||\tTimer Timer:start passes arguments to on_tick function\t", + "Success\t||\tTimer Timer:start stops timer when on_tick returns false\t", + "Success\t||\tTimer Timer:start stops timer when on_tick throws an error\t", + "Success\t||\tTimer Timer:start stops previous timer before starting new one\t", + "Success\t||\tTimer Timer:start throws error when timer creation fails\t", + "Success\t||\tTimer Timer:stop stops a running timer\t", + "Success\t||\tTimer Timer:stop calls on_stop callback when provided\t", + "Success\t||\tTimer Timer:stop does nothing when timer is not running\t", + "Success\t||\tTimer Timer:stop handles errors in on_stop callback gracefully\t", + "Success\t||\tTimer Timer:is_running returns false when timer is not started\t", + "Success\t||\tTimer Timer:is_running returns true when timer is running\t", + "Success\t||\tTimer Timer:is_running returns false after timer is stopped\t", + "Success\t||\tTimer Timer:is_running returns false after one-shot timer completes\t", + "Success\t||\tTimer Integration tests can restart a stopped timer\t", + "Success\t||\tTimer Integration tests handles rapid start/stop cycles\t", + "\t", + "Success: \t20\t", + "Failed : \t0\t", + "========================================\t", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "Two pending permissions? existing: per_9efb5b2f3001aqJAFBMiGjFjVZ new: per_9efb5bc2a001j9Bd6bFjLB7hrc", + "Two pending permissions? existing: per_9efb5bc2a001j9Bd6bFjLB7hrc new: per_9efb5d6d1001uwVXQ9dhlBlgfO", + "", + "========================================\t", + "Testing: \t/home/francis/Projects/_nvim/opencode.nvim/tests/unit/renderer_spec.lua\t", + "Success\t||\trenderer replays ansi-codes correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays ansi-codes correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays ansi-codes correctly (session)\t", + "Success\t||\trenderer replays api-abort correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays api-abort correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays api-abort correctly (session)\t", + "Success\t||\trenderer replays api-error correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays api-error correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays api-error correctly (session)\t", + "Success\t||\trenderer replays cursor_data correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays cursor_data correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays cursor_data correctly (session)\t", + "Success\t||\trenderer replays diff correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays diff correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays diff correctly (session)\t", + "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays markdown-codefence correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays markdown-codefence correctly (session)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays mentions-with-ranges correctly (session)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays message-removal correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays perf correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays perf correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays perf correctly (session)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission-denied correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission-denied correctly (session)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission-prompt correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays permission correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays permission correctly (session)\t", + "Success\t||\trenderer replays planning correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays planning correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays planning correctly (session)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays redo-all correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays redo-all correctly (session)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays redo-once correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays redo-once correctly (session)\t", + "Success\t||\trenderer replays revert correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays revert correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays revert correctly (session)\t", + "Success\t||\trenderer replays selection correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays selection correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays selection correctly (session)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays shifting-and-multiple-perms correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays simple-session correctly (session)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays tool-invalid correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays tool-invalid correctly (session)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event, collapsing)\t", + "Success\t||\trenderer replays updating-text correctly (event-by-event, no collapsing)\t", + "Success\t||\trenderer replays updating-text correctly (session)\t", + "\t", + "Success: \t60\t", + "Failed : \t0\t", + "========================================\t", + "✓ Unit tests passed", + "------------------------------------------------", + "All tests passed successfully!", + "", + "`````", + "", + "----", + "", + "", + "All steps are complete and all tests pass! Here’s a summary of what was fixed:", + "", + "- ✅ Added a nil check for `state.active_session` before accessing `.id` in `M.send_message`.", + "- ✅ Ensured the `model` field in `params` matches the expected type `{ modelID = ..., providerID = ... }`.", + "- ✅ Verified the fixes by running the full test suite—no errors or failures.", + "", + "Your file is now robust and EmmyLua diagnostics at line 130 are resolved. If you need further tweaks or want to address other diagnostics, let me know!", + "", + "" ], - "timestamp": 1769172116 -} + "timestamp": 1770748574 +} \ No newline at end of file diff --git a/tests/data/diff.expected.json b/tests/data/diff.expected.json index 547ab7dc..5303cfd8 100644 --- a/tests/data/diff.expected.json +++ b/tests/data/diff.expected.json @@ -1,4 +1,45 @@ { + "actions": [ + { + "args": [ + "1f593f7ed419c95d3995f8ef4b98d4e571c3a492" + ], + "display_line": 18, + "key": "R", + "range": { + "from": 18, + "to": 18 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "1f593f7ed419c95d3995f8ef4b98d4e571c3a492" + ], + "display_line": 18, + "key": "A", + "range": { + "from": 18, + "to": 18 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "1f593f7ed419c95d3995f8ef4b98d4e571c3a492" + ], + "display_line": 18, + "key": "D", + "range": { + "from": 18, + "to": 18 + }, + "text": "[D]iff", + "type": "diff_open" + } + ], "extmarks": [ [ 1, @@ -7,19 +48,36 @@ { "ns_id": 3, "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-12 06:42:56)", "OpencodeHint"], - [" [msg_9d7287269001C5gRusYfX7A1w1]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-12 06:42:56)", + "OpencodeHint" + ], + [ + " [msg_9d7287269001C5gRusYfX7A1w1]", + "OpencodeHint" + ] ], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "virt_text_hide": false, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -29,12 +87,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -44,12 +107,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -57,14 +125,14 @@ 3, 39, { - "ns_id": 3, - "hl_group": "OpencodeMention", - "end_row": 3, "end_col": 53, "end_right_gravity": false, + "end_row": 3, + "hl_eol": false, + "hl_group": "OpencodeMention", + "ns_id": 3, "priority": 1000, - "right_gravity": true, - "hl_eol": false + "right_gravity": true } ], [ @@ -74,12 +142,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -89,12 +162,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -104,19 +182,36 @@ { "ns_id": 3, "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-12 06:42:56)", "OpencodeHint"], - [" [msg_9d7287287001HVwpPaH7WkRVdN]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-12 06:42:56)", + "OpencodeHint" + ], + [ + " [msg_9d7287287001HVwpPaH7WkRVdN]", + "OpencodeHint" + ] ], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "virt_text_hide": false, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -126,12 +221,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -141,12 +241,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -156,12 +261,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -169,17 +279,22 @@ 13, 0, { - "ns_id": 3, "end_col": 0, - "virt_text_pos": "overlay", - "virt_text": [["-", "OpencodeDiffDelete"]], - "priority": 5000, - "hl_group": "OpencodeDiffDelete", + "end_right_gravity": false, "end_row": 14, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false, + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -190,12 +305,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -203,17 +323,22 @@ 14, 0, { - "ns_id": 3, "end_col": 0, - "virt_text_pos": "overlay", - "virt_text": [["+", "OpencodeDiffAdd"]], - "priority": 5000, - "hl_group": "OpencodeDiffAdd", + "end_right_gravity": false, "end_row": 15, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -224,12 +349,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -239,12 +369,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -254,12 +389,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -269,49 +409,39 @@ { "ns_id": 3, "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-12 06:43:03)", "OpencodeHint"], - [" [msg_9d7288f2f001hW6NqqhtBc72UU]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-12 06:43:03)", + "OpencodeHint" + ], + [ + " [msg_9d7288f2f001hW6NqqhtBc72UU]", + "OpencodeHint" + ] ], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "virt_text_hide": false, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "timestamp": 1769172144, - "actions": [ - { - "key": "R", - "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], - "display_line": 18, - "range": { "from": 18, "to": 18 }, - "type": "diff_revert_selected_file", - "text": "[R]evert file" - }, - { - "key": "A", - "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], - "display_line": 18, - "range": { "from": 18, "to": 18 }, - "type": "diff_revert_all", - "text": "Revert [A]ll" - }, - { - "key": "D", - "args": ["1f593f7ed419c95d3995f8ef4b98d4e571c3a492"], - "display_line": 18, - "range": { "from": 18, "to": 18 }, - "type": "diff_open", - "text": "[D]iff" - } - ], "lines": [ "----", "", @@ -337,5 +467,6 @@ "", "", "" - ] -} + ], + "timestamp": 1770748574 +} \ No newline at end of file diff --git a/tests/data/markdown-codefence.expected.json b/tests/data/markdown-codefence.expected.json index 755db80c..36e74b11 100644 --- a/tests/data/markdown-codefence.expected.json +++ b/tests/data/markdown-codefence.expected.json @@ -1,83 +1,44 @@ { - "lines": [ - "----", - "", - "", - "Now let me also update the Run opts examples to use the new command syntax:", - "", - "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/README.md`", - "", - "`````markdown", - " ", - " Run a prompt in a new session using the Plan agent and disabling current file context:", - " ", - " ```vim", - " :OpencodeRunNewSession \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", - " :OpencodeRun \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", - " :Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", - " :Opencode run \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", - " ```", - " ", - " ##👮 Permissions", - " ", - "", - "`````", - "", - "**󰻛 Created Snapshot** `c64ddee8`", - "", - "----", - "", - "", - "README updated with the new nested command structure. The documentation now shows the new `:Opencode` subcommand syntax and includes a note that legacy `Opencode*` commands are still available by default but will be removed in the future.", - "", - "----", - "", - "", - "can you rename Opencode stop to Opencode cancel (and rename the api call)", - "", - "[`README.md`](README.md)", - "", - "----", - "", - "", - "Here's the plan to rename `Opencode stop` to `Opencode cancel` and update the API call accordingly:", - "", - "---", - "", - "## 📝 Plan: Rename Opencode Stop to Cancel", - "", - "### 1. Update Command Table and Documentation", - "- In  README.md, change:", - " - Command: `:Opencode stop` → `:Opencode cancel`", - " - API: `require('opencode.api').stop()` → `require('opencode.api').cancel()`", - " - Keymap: `` remains the same (unless you want to change it)", - "", - "### 2. Update Codebase", - "- In  `lua/opencode/api.lua`:", - " - Rename the function `M.stop()` to `M.cancel()`", - " - Update all references to `stop` (command registration, legacy command map, subcommand routing, etc.) to use `cancel`", - " - Ensure legacy command `OpencodeStop` still works (with deprecation warning), but routes to `cancel`", - "- In any other files (keymap config, tests, etc.) update references to `stop` to `cancel` as needed", - "", - "### 3. Update Tests", - "- In  `tests/unit/api_spec.lua`:", - " - Update any tests that check for `stop` to check for `cancel`", - "", - "### 4. Update Slash Commands (if applicable)", - "- If there is a `/stop` slash command, rename it to `/cancel`", - "", - "### 5. Verify", - "- Run all tests to ensure everything works", - "- Check that both `:Opencode cancel` and legacy `:OpencodeStop` work (with warning)", - "", - "---", - "", - "**Note:** You are currently in \"Plan mode\" (read-only phase), so I cannot make edits right now. ", - "When you switch to edit mode, I will execute this plan step by step and verify all changes.", - "", - "Would you like to proceed with this plan when edits are allowed?", - "", - "" + "actions": [ + { + "args": [ + "c64ddee834f1b802020a8f880eafa689f0b99406" + ], + "display_line": 23, + "key": "R", + "range": { + "from": 23, + "to": 23 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "c64ddee834f1b802020a8f880eafa689f0b99406" + ], + "display_line": 23, + "key": "A", + "range": { + "from": 23, + "to": 23 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "c64ddee834f1b802020a8f880eafa689f0b99406" + ], + "display_line": 23, + "key": "D", + "range": { + "from": 23, + "to": 23 + }, + "text": "[D]iff", + "type": "diff_open" + } ], "extmarks": [ [ @@ -86,20 +47,37 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": false, "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-28 21:53:58)", "OpencodeHint"], - [" [msg_a2cd04588001P9plKmrFnsNH3M]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-28 21:53:58)", + "OpencodeHint" + ], + [ + " [msg_a2cd04588001P9plKmrFnsNH3M]", + "OpencodeHint" + ] ], - "right_gravity": true + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -108,13 +86,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -123,13 +106,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -138,13 +126,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -153,13 +146,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -168,13 +166,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -183,13 +186,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -198,13 +206,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -212,18 +225,23 @@ 12, 0, { - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 5000, - "virt_text": [["-", "OpencodeDiffDelete"]], - "ns_id": 3, - "hl_group": "OpencodeDiffDelete", - "right_gravity": true, "end_col": 0, + "end_right_gravity": false, "end_row": 13, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, + "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -232,13 +250,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -246,18 +269,23 @@ 13, 0, { - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 5000, - "virt_text": [["-", "OpencodeDiffDelete"]], - "ns_id": 3, - "hl_group": "OpencodeDiffDelete", - "right_gravity": true, "end_col": 0, + "end_right_gravity": false, "end_row": 14, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, + "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -266,13 +294,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -280,18 +313,23 @@ 14, 0, { - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 5000, - "virt_text": [["+", "OpencodeDiffAdd"]], - "ns_id": 3, - "hl_group": "OpencodeDiffAdd", - "right_gravity": true, "end_col": 0, + "end_right_gravity": false, "end_row": 15, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, + "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -300,13 +338,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -314,18 +357,23 @@ 15, 0, { - "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 5000, - "virt_text": [["+", "OpencodeDiffAdd"]], - "ns_id": 3, - "hl_group": "OpencodeDiffAdd", - "right_gravity": true, "end_col": 0, + "end_right_gravity": false, "end_row": 16, "hl_eol": true, - "end_right_gravity": false, - "virt_text_hide": false + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, + "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -334,13 +382,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -349,13 +402,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -364,13 +422,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -379,13 +442,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -394,13 +462,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -409,13 +482,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -424,13 +502,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -1, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -439,20 +522,37 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": false, "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-28 21:54:05)", "OpencodeHint"], - [" [msg_a2cd062fb001UA0ZzR6JxgLxDQ]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-28 21:54:05)", + "OpencodeHint" + ], + [ + " [msg_a2cd062fb001UA0ZzR6JxgLxDQ]", + "OpencodeHint" + ] ], - "right_gravity": true + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -461,20 +561,37 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": false, "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-28 22:34:58)", "OpencodeHint"], - [" [msg_a2cf5ce65001YLvVsYxIboFcP4]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-28 22:34:58)", + "OpencodeHint" + ], + [ + " [msg_a2cf5ce65001YLvVsYxIboFcP4]", + "OpencodeHint" + ] ], - "right_gravity": true + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -483,13 +600,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -3, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -498,13 +620,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -3, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -513,13 +640,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -3, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -528,13 +660,18 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": true, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_win_col": -3, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -543,48 +680,119 @@ 0, { "ns_id": 3, - "virt_text_repeat_linebreak": false, "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-10-28 22:34:58)", "OpencodeHint"], - [" [msg_a2cf5cf0f002oBAjmnGIMGXjez]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-10-28 22:34:58)", + "OpencodeHint" + ], + [ + " [msg_a2cf5cf0f002oBAjmnGIMGXjez]", + "OpencodeHint" + ] ], - "right_gravity": true + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [ - { - "text": "[R]evert file", - "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], - "type": "diff_revert_selected_file", - "key": "R", - "display_line": 23, - "range": { "to": 23, "from": 23 } - }, - { - "text": "Revert [A]ll", - "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], - "type": "diff_revert_all", - "key": "A", - "display_line": 23, - "range": { "to": 23, "from": 23 } - }, - { - "text": "[D]iff", - "args": ["c64ddee834f1b802020a8f880eafa689f0b99406"], - "type": "diff_open", - "key": "D", - "display_line": 23, - "range": { "to": 23, "from": 23 } - } + "lines": [ + "----", + "", + "", + "Now let me also update the Run opts examples to use the new command syntax:", + "", + "** edit** `/Users/cam/Dev/neovim-dev/opencode.nvim/README.md`", + "", + "`````markdown", + " ", + " Run a prompt in a new session using the Plan agent and disabling current file context:", + " ", + " ```vim", + " :OpencodeRunNewSession \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", + " :OpencodeRun \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", + " :Opencode run new_session \"Please help me plan a new feature\" agent=plan context.current_file.enabled=false", + " :Opencode run \"Fix the bug in the current file\" model=github-copilot/claude-sonned-4", + " ```", + " ", + " ##👮 Permissions", + " ", + "", + "`````", + "", + "**󰻛 Created Snapshot** `c64ddee8`", + "", + "----", + "", + "", + "README updated with the new nested command structure. The documentation now shows the new `:Opencode` subcommand syntax and includes a note that legacy `Opencode*` commands are still available by default but will be removed in the future.", + "", + "----", + "", + "", + "can you rename Opencode stop to Opencode cancel (and rename the api call)", + "", + "[`README.md`](README.md)", + "", + "----", + "", + "", + "Here's the plan to rename `Opencode stop` to `Opencode cancel` and update the API call accordingly:", + "", + "---", + "", + "## 📝 Plan: Rename Opencode Stop to Cancel", + "", + "### 1. Update Command Table and Documentation", + "- In  README.md, change:", + " - Command: `:Opencode stop` → `:Opencode cancel`", + " - API: `require('opencode.api').stop()` → `require('opencode.api').cancel()`", + " - Keymap: `` remains the same (unless you want to change it)", + "", + "### 2. Update Codebase", + "- In  `lua/opencode/api.lua`:", + " - Rename the function `M.stop()` to `M.cancel()`", + " - Update all references to `stop` (command registration, legacy command map, subcommand routing, etc.) to use `cancel`", + " - Ensure legacy command `OpencodeStop` still works (with deprecation warning), but routes to `cancel`", + "- In any other files (keymap config, tests, etc.) update references to `stop` to `cancel` as needed", + "", + "### 3. Update Tests", + "- In  `tests/unit/api_spec.lua`:", + " - Update any tests that check for `stop` to check for `cancel`", + "", + "### 4. Update Slash Commands (if applicable)", + "- If there is a `/stop` slash command, rename it to `/cancel`", + "", + "### 5. Verify", + "- Run all tests to ensure everything works", + "- Check that both `:Opencode cancel` and legacy `:OpencodeStop` work (with warning)", + "", + "---", + "", + "**Note:** You are currently in \"Plan mode\" (read-only phase), so I cannot make edits right now. ", + "When you switch to edit mode, I will execute this plan step by step and verify all changes.", + "", + "Would you like to proceed with this plan when edits are allowed?", + "", + "" ], - "timestamp": 1769172150 -} + "timestamp": 1770748574 +} \ No newline at end of file diff --git a/tests/data/mentions-with-ranges.expected.json b/tests/data/mentions-with-ranges.expected.json index 789362b0..2d776a92 100644 --- a/tests/data/mentions-with-ranges.expected.json +++ b/tests/data/mentions-with-ranges.expected.json @@ -6,21 +6,38 @@ 1, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-12 23:38:21)", "OpencodeHint"], - [" [msg_9daca16bf0017x95VD45mw3k8Q]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-12 23:38:21)", + "OpencodeHint" + ], + [ + " [msg_9daca16bf0017x95VD45mw3k8Q]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10, - "ns_id": 3, - "virt_text_win_col": -3, - "virt_text_hide": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -28,14 +45,19 @@ 2, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -43,14 +65,19 @@ 3, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -58,14 +85,14 @@ 3, 5, { - "right_gravity": true, - "end_right_gravity": false, - "ns_id": 3, - "priority": 1000, "end_col": 44, + "end_right_gravity": false, "end_row": 3, "hl_eol": false, - "hl_group": "OpencodeMention" + "hl_group": "OpencodeMention", + "ns_id": 3, + "priority": 1000, + "right_gravity": true } ], [ @@ -73,14 +100,19 @@ 4, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -88,14 +120,19 @@ 5, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -103,14 +140,14 @@ 5, 23, { - "right_gravity": true, - "end_right_gravity": false, - "ns_id": 3, - "priority": 1000, "end_col": 48, + "end_right_gravity": false, "end_row": 5, "hl_eol": false, - "hl_group": "OpencodeMention" + "hl_group": "OpencodeMention", + "ns_id": 3, + "priority": 1000, + "right_gravity": true } ], [ @@ -118,14 +155,19 @@ 6, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -133,14 +175,19 @@ 7, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -148,14 +195,19 @@ 8, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -163,14 +215,19 @@ 9, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -178,14 +235,19 @@ 10, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -193,14 +255,19 @@ 11, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -208,14 +275,19 @@ 12, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -223,14 +295,19 @@ 13, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -238,14 +315,19 @@ 14, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -253,14 +335,19 @@ 15, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -268,14 +355,19 @@ 16, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -283,14 +375,19 @@ 17, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -298,14 +395,19 @@ 18, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -313,14 +415,19 @@ 19, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -328,14 +435,19 @@ 20, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -343,18 +455,22 @@ 21, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": true, - "priority": 4096, "ns_id": 3, - "virt_text_win_col": -3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "right_gravity": true + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ] ], - "timestamp": 1766432124, "lines": [ "----", "", @@ -380,5 +496,6 @@ "[`lua/opencode/ui/session_formatter.lua`](lua/opencode/ui/session_formatter.lua)", "", "" - ] -} + ], + "timestamp": 1770748575 +} \ No newline at end of file diff --git a/tests/data/message-removal.expected.json b/tests/data/message-removal.expected.json index 80c014ae..369ffb5d 100644 --- a/tests/data/message-removal.expected.json +++ b/tests/data/message-removal.expected.json @@ -1,5 +1,4 @@ { - "timestamp": 1766432124, "actions": [], "extmarks": [ [ @@ -7,21 +6,38 @@ 1, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-09 08:53:21)", "OpencodeHint"], - [" [msg_001]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-09 08:53:21)", + "OpencodeHint" + ], + [ + " [msg_001]", + "OpencodeHint" + ] ], "virt_text_hide": false, - "ns_id": 3, - "right_gravity": true, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -29,14 +45,19 @@ 2, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -44,14 +65,19 @@ 3, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -59,14 +85,19 @@ 4, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -74,14 +105,19 @@ 5, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -89,14 +125,19 @@ 6, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -104,14 +145,19 @@ 7, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -119,14 +165,19 @@ 8, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -134,14 +185,19 @@ 9, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -149,21 +205,38 @@ 12, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - ["", "OpencodeHint"], - [" (2025-10-09 08:53:22)", "OpencodeHint"], - [" [msg_002]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-09 08:53:22)", + "OpencodeHint" + ], + [ + " [msg_002]", + "OpencodeHint" + ] ], "virt_text_hide": false, - "ns_id": 3, - "right_gravity": true, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -171,21 +244,38 @@ 21, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - ["", "OpencodeHint"], - [" (2025-10-09 08:53:24)", "OpencodeHint"], - [" [msg_004]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-09 08:53:24)", + "OpencodeHint" + ], + [ + " [msg_004]", + "OpencodeHint" + ] ], "virt_text_hide": false, - "ns_id": 3, - "right_gravity": true, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], @@ -218,5 +308,6 @@ "Message 4, Part 5", "", "" - ] -} + ], + "timestamp": 1770748575 +} \ No newline at end of file diff --git a/tests/data/multiple-messages-synthetic.expected.json b/tests/data/multiple-messages-synthetic.expected.json index 939536d7..6ef78f06 100644 --- a/tests/data/multiple-messages-synthetic.expected.json +++ b/tests/data/multiple-messages-synthetic.expected.json @@ -1,75 +1,43 @@ { - "timestamp": 1766432125, - "lines": [ - "----", - "", - "", - "Message 1", - "", - "----", - "", - "", - "Response 2 - Part 1", - "", - "Response 2 - Part 2", - "", - "Response 2 - Part 3", - "", - "Response 2 - Part 4 (late arrival)", - "", - "----", - "", - "", - "Message 3", - "", - "----", - "", - "", - "Response 4 - Part 1", - "", - "Response 4 - Part 2", - "", - "Response 4 - Part 3", - "", - "Response 4 - Part 4 (late arrival)", - "", - "----", - "", - "", - "Message 5", - "", - "----", - "", - "", - "Response 6 - Part 1", - "", - "Response 6 - Part 2", - "", - "Response 6 - Part 3", - "", - "" - ], + "actions": [], "extmarks": [ [ 1, 1, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:41)", "OpencodeHint"], - [" [msg_001]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:41)", + "OpencodeHint" + ], + [ + " [msg_001]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -77,14 +45,19 @@ 2, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -92,14 +65,19 @@ 3, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -107,21 +85,38 @@ 6, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:42)", "OpencodeHint"], - [" [msg_002]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:42)", + "OpencodeHint" + ], + [ + " [msg_002]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -129,21 +124,38 @@ 17, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:43)", "OpencodeHint"], - [" [msg_003]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:43)", + "OpencodeHint" + ], + [ + " [msg_003]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -151,14 +163,19 @@ 18, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -166,14 +183,19 @@ 19, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -181,21 +203,38 @@ 22, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:44)", "OpencodeHint"], - [" [msg_004]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:44)", + "OpencodeHint" + ], + [ + " [msg_004]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -203,21 +242,38 @@ 33, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:45)", "OpencodeHint"], - [" [msg_005]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:45)", + "OpencodeHint" + ], + [ + " [msg_005]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -225,14 +281,19 @@ 34, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -240,14 +301,19 @@ 35, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -255,23 +321,89 @@ 38, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - ["", "OpencodeHint"], - [" (2001-09-09 01:46:46)", "OpencodeHint"], - [" [msg_006]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2001-09-09 01:46:46)", + "OpencodeHint" + ], + [ + " [msg_006]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10 + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [] -} + "lines": [ + "----", + "", + "", + "Message 1", + "", + "----", + "", + "", + "Response 2 - Part 1", + "", + "Response 2 - Part 2", + "", + "Response 2 - Part 3", + "", + "Response 2 - Part 4 (late arrival)", + "", + "----", + "", + "", + "Message 3", + "", + "----", + "", + "", + "Response 4 - Part 1", + "", + "Response 4 - Part 2", + "", + "Response 4 - Part 3", + "", + "Response 4 - Part 4 (late arrival)", + "", + "----", + "", + "", + "Message 5", + "", + "----", + "", + "", + "Response 6 - Part 1", + "", + "Response 6 - Part 2", + "", + "Response 6 - Part 3", + "", + "" + ], + "timestamp": 1770748575 +} \ No newline at end of file diff --git a/tests/data/multiple-messages.expected.json b/tests/data/multiple-messages.expected.json index 59334242..786f5912 100644 --- a/tests/data/multiple-messages.expected.json +++ b/tests/data/multiple-messages.expected.json @@ -1,124 +1,43 @@ { "actions": [], - "lines": [ - "----", - "", - "", - "can you count to 10?", - "", - "[`a-empty.txt`](a-empty.txt)", - "", - "----", - "", - "", - "Sure! Here is a count from 1 to 10:", - "", - "1 ", - "2 ", - "3 ", - "4 ", - "5 ", - "6 ", - "7 ", - "8 ", - "9 ", - "10", - "", - "Let me know if you need anything else!", - "", - "----", - "", - "", - "can you say all of the letters?", - "", - "[`a-empty.txt`](a-empty.txt)", - "", - "----", - "", - "", - "Absolutely! Here are all the letters of the English alphabet:", - "", - "A ", - "B ", - "C ", - "D ", - "E ", - "F ", - "G ", - "H ", - "I ", - "J ", - "K ", - "L ", - "M ", - "N ", - "O ", - "P ", - "Q ", - "R ", - "S ", - "T ", - "U ", - "V ", - "W ", - "X ", - "Y ", - "Z", - "", - "If you need lowercase, here they are as well:", - "", - "a ", - "b ", - "c ", - "d ", - "e ", - "f ", - "g ", - "h ", - "i ", - "j ", - "k ", - "l ", - "m ", - "n ", - "o ", - "p ", - "q ", - "r ", - "s ", - "t ", - "u ", - "v ", - "w ", - "x ", - "y ", - "z", - "", - "Let me know if you need anything else!", - "", - "" - ], "extmarks": [ [ 1, 1, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-11-01 03:32:54)", "OpencodeHint"], - [" [msg_a3d79a71f001dkQYw23QnYYElB]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-11-01 03:32:54)", + "OpencodeHint" + ], + [ + " [msg_a3d79a71f001dkQYw23QnYYElB]", + "OpencodeHint" + ] ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -126,14 +45,19 @@ 2, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -141,14 +65,19 @@ 3, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -156,14 +85,19 @@ 4, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -171,14 +105,19 @@ 5, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -186,21 +125,38 @@ 8, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-01 03:32:54)", "OpencodeHint"], - [" [msg_a3d79a771002NY7n6a0vJlriLH]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-01 03:32:54)", + "OpencodeHint" + ], + [ + " [msg_a3d79a771002NY7n6a0vJlriLH]", + "OpencodeHint" + ] ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -208,21 +164,38 @@ 26, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-11-01 03:32:55)", "OpencodeHint"], - [" [msg_a3d79aa5b0014RYY4My2AeTokd]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-11-01 03:32:55)", + "OpencodeHint" + ], + [ + " [msg_a3d79aa5b0014RYY4My2AeTokd]", + "OpencodeHint" + ] ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -230,14 +203,19 @@ 27, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -245,14 +223,19 @@ 28, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -260,14 +243,19 @@ 29, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -275,14 +263,19 @@ 30, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ], [ @@ -290,23 +283,138 @@ 33, 0, { - "virt_text_hide": false, - "right_gravity": true, "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-11-01 03:32:56)", "OpencodeHint"], - [" [msg_a3d79ae610029QFKwb1uvSa4Lf]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-11-01 03:32:56)", + "OpencodeHint" + ], + [ + " [msg_a3d79ae610029QFKwb1uvSa4Lf]", + "OpencodeHint" + ] ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col" + "virt_text_win_col": -3 } ] ], - "timestamp": 1766432125 -} + "lines": [ + "----", + "", + "", + "can you count to 10?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "Sure! Here is a count from 1 to 10:", + "", + "1 ", + "2 ", + "3 ", + "4 ", + "5 ", + "6 ", + "7 ", + "8 ", + "9 ", + "10", + "", + "Let me know if you need anything else!", + "", + "----", + "", + "", + "can you say all of the letters?", + "", + "[`a-empty.txt`](a-empty.txt)", + "", + "----", + "", + "", + "Absolutely! Here are all the letters of the English alphabet:", + "", + "A ", + "B ", + "C ", + "D ", + "E ", + "F ", + "G ", + "H ", + "I ", + "J ", + "K ", + "L ", + "M ", + "N ", + "O ", + "P ", + "Q ", + "R ", + "S ", + "T ", + "U ", + "V ", + "W ", + "X ", + "Y ", + "Z", + "", + "If you need lowercase, here they are as well:", + "", + "a ", + "b ", + "c ", + "d ", + "e ", + "f ", + "g ", + "h ", + "i ", + "j ", + "k ", + "l ", + "m ", + "n ", + "o ", + "p ", + "q ", + "r ", + "s ", + "t ", + "u ", + "v ", + "w ", + "x ", + "y ", + "z", + "", + "Let me know if you need anything else!", + "", + "" + ], + "timestamp": 1770748575 +} \ No newline at end of file diff --git a/tests/data/multiple-question-ask-reply-all.expected.json b/tests/data/multiple-question-ask-reply-all.expected.json index c39fbdb5..6872b4b9 100644 --- a/tests/data/multiple-question-ask-reply-all.expected.json +++ b/tests/data/multiple-question-ask-reply-all.expected.json @@ -1,66 +1,43 @@ { - "lines": [ - "----", - "", - "", - "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", - "", - "can you use the question tool and ask me a couple of questions", - "", - "----", - "", - "", - "** question** ", - "", - "**Q1:** Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", - "**A1:** busted", - "", - "**Q2:** How do you usually run your tests in this codebase?", - "**A2:** run_tests.sh (Recommended)", - "", - "**Q3:** multi-line question", - "The most important question of all:", - "Which team are you on?", - "**A3:** Coffee", - "", - "**󰻛 Created Snapshot** `be64b620`", - "", - "----", - "", - "", - "Thanks for your answers! You've indicated:", - "", - "- Preferred testing framework: **busted**", - "- Typical test running method: ** run_tests.sh (Recommended)**", - "", - "I have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!", - "", - "" - ], "actions": [ { + "args": [ + "be64b6205f0da4c9240df2476dc35495429161b5" + ], "display_line": 23, - "range": { "from": 23, "to": 23 }, - "type": "diff_revert_selected_file", - "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "key": "R", + "range": { + "from": 23, + "to": 23 + }, "text": "[R]evert file", - "key": "R" + "type": "diff_revert_selected_file" }, { + "args": [ + "be64b6205f0da4c9240df2476dc35495429161b5" + ], "display_line": 23, - "range": { "from": 23, "to": 23 }, - "type": "diff_revert_all", - "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "key": "A", + "range": { + "from": 23, + "to": 23 + }, "text": "Revert [A]ll", - "key": "A" + "type": "diff_revert_all" }, { + "args": [ + "be64b6205f0da4c9240df2476dc35495429161b5" + ], "display_line": 23, - "range": { "from": 23, "to": 23 }, - "type": "diff_open", - "args": ["be64b6205f0da4c9240df2476dc35495429161b5"], + "key": "D", + "range": { + "from": 23, + "to": 23 + }, "text": "[D]iff", - "key": "D" + "type": "diff_open" } ], "extmarks": [ @@ -70,20 +47,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 10, "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-26 14:30:46)", "OpencodeHint"], - [" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-26 14:30:46)", + "OpencodeHint" + ], + [ + " [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -92,13 +86,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -107,13 +106,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -122,13 +126,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -137,13 +146,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -152,13 +166,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -167,20 +186,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 10, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:30:46)", "OpencodeHint"], - [" [msg_bfab6dc80001FueCN7E2691J2R]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:30:46)", + "OpencodeHint" + ], + [ + " [msg_bfab6dc80001FueCN7E2691J2R]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -189,13 +225,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -204,13 +245,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -219,13 +265,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -234,13 +285,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -249,13 +305,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -264,13 +325,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -279,13 +345,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -294,13 +365,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -309,13 +385,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -324,13 +405,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -339,13 +425,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -354,13 +445,18 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -369,22 +465,77 @@ 0, { "ns_id": 3, - "virt_text_hide": false, + "priority": 10, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:31:55)", "OpencodeHint"], - [" [msg_bfab7e7fd0018kA9yLHMkJM3fA]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:31:55)", + "OpencodeHint" + ], + [ + " [msg_bfab7e7fd0018kA9yLHMkJM3fA]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "timestamp": 1770681185 -} + "lines": [ + "----", + "", + "", + "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", + "", + "can you use the question tool and ask me a couple of questions", + "", + "----", + "", + "", + "** question** ", + "", + "**Q1:** Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "**A1:** busted", + "", + "**Q2:** How do you usually run your tests in this codebase?", + "**A2:** run_tests.sh (Recommended)", + "", + "**Q3:** multi-line question", + "The most important question of all:", + "Which team are you on?", + "**A3:** Coffee", + "", + "**󰻛 Created Snapshot** `be64b620`", + "", + "----", + "", + "", + "Thanks for your answers! You've indicated:", + "", + "- Preferred testing framework: **busted**", + "- Typical test running method: ** run_tests.sh (Recommended)**", + "", + "I have this in mind for any future testing, troubleshooting, or automation tasks you request for this Neovim Lua codebase. If you have any follow-up questions or tasks (such as test writing, modification, running, debugging, or reporting), let me know—I'll tailor my workflow to these preferences for maximal compatibility and consistency!", + "", + "" + ], + "timestamp": 1770748576 +} \ No newline at end of file diff --git a/tests/data/multiple-question-ask.expected.json b/tests/data/multiple-question-ask.expected.json index 5849dc4b..562af240 100644 --- a/tests/data/multiple-question-ask.expected.json +++ b/tests/data/multiple-question-ask.expected.json @@ -1,32 +1,4 @@ { - "lines": [ - "----", - "", - "", - "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", - "", - "can you use the question tool and ask me a couple of questions", - "", - "----", - "", - "", - "** question** ", - "", - "----", - "", - "", - " Question (1/2)", - "", - "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", - "", - " 1. busted - Busted is the most common Lua testing framework. ", - " 2. plenary.nvim - Plenary.nvim provides unit test utilities for Neovim plugins.", - " 3. other - Other or custom test framework", - "", - "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``", - "", - "" - ], "actions": [], "extmarks": [ [ @@ -34,21 +6,38 @@ 1, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col", "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-26 14:30:46)", "OpencodeHint"], - [" [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-26 14:30:46)", + "OpencodeHint" + ], + [ + " [msg_bfab6dbf3001ZOVHTFKR1CMUE5]", + "OpencodeHint" + ] ], - "ns_id": 3 + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -56,14 +45,19 @@ 2, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -71,14 +65,19 @@ 3, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -86,14 +85,19 @@ 4, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -101,14 +105,19 @@ 5, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -116,14 +125,19 @@ 6, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -131,21 +145,38 @@ 8, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col", "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:30:46)", "OpencodeHint"], - [" [msg_bfab6dc80001FueCN7E2691J2R]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:30:46)", + "OpencodeHint" + ], + [ + " [msg_bfab6dc80001FueCN7E2691J2R]", + "OpencodeHint" + ] ], - "ns_id": 3 + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -153,37 +184,69 @@ 13, 0, { - "virt_text_win_col": -3, + "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col", "virt_text": [ - [" ", "OpencodeMessageRoleSystem"], - [" "], - ["SYSTEM", "OpencodeMessageRoleSystem"], - ["", "OpencodeHint"], - ["", "OpencodeHint"], - [" [question-display-message]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleSystem" + ], + [ + " " + ], + [ + "SYSTEM", + "OpencodeMessageRoleSystem" + ], + [ + "", + "OpencodeHint" + ], + [ + "", + "OpencodeHint" + ], + [ + " [question-display-message]", + "OpencodeHint" + ] ], - "ns_id": 3 + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ], + [ + 9, + 15, + 0, + { + "line_hl_group": "OpencodeQuestionTitle", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [9, 15, 0, { "line_hl_group": "OpencodeQuestionTitle", "priority": 4096, "right_gravity": true, "ns_id": 3 }], [ 10, 15, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -191,14 +254,19 @@ 16, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -206,14 +274,19 @@ 17, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -221,30 +294,50 @@ 18, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 14, + 19, + 0, + { + "line_hl_group": "OpencodeDialogOptionHover", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [14, 19, 0, { "line_hl_group": "OpencodeDialogOptionHover", "priority": 4096, "right_gravity": true, "ns_id": 3 }], [ 15, 19, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -252,13 +345,18 @@ 19, 2, { + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": false, + "virt_text": [ + [ + "› ", + "OpencodeDialogOptionHover" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "virt_text": [["› ", "OpencodeDialogOptionHover"]], - "ns_id": 3 + "virt_text_repeat_linebreak": false } ], [ @@ -266,14 +364,19 @@ 20, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -281,14 +384,19 @@ 21, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -296,14 +404,19 @@ 22, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -311,16 +424,49 @@ 23, 0, { - "virt_text_win_col": -2, + "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text_repeat_linebreak": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeQuestionBorder"]], - "ns_id": 3 + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ] ], - "timestamp": 1769441953 -} + "lines": [ + "----", + "", + "", + "[`tests/replay/renderer_spec.lua`](tests/replay/renderer_spec.lua)", + "", + "can you use the question tool and ask me a couple of questions", + "", + "----", + "", + "", + "** question** ", + "", + "----", + "", + "", + " Question (1/2)", + "", + "Which Lua testing framework do you use or prefer for your Neovim Lua plugins?", + "", + " 1. busted - Busted is the most common Lua testing framework. ", + " 2. plenary.nvim - Plenary.nvim provides unit test utilities for Neovim plugins.", + " 3. other - Other or custom test framework", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3` Dismiss: ``", + "", + "" + ], + "timestamp": 1770748576 +} \ No newline at end of file diff --git a/tests/data/perf.expected.json b/tests/data/perf.expected.json index 47fc23de..9f981cb1 100644 --- a/tests/data/perf.expected.json +++ b/tests/data/perf.expected.json @@ -1,25 +1,43 @@ { + "actions": [], "extmarks": [ [ 1, 1, 0, { - "virt_text_repeat_linebreak": false, + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-24 19:32:00)", "OpencodeHint"], - [" [msg_a17b4dc4c001x19oFZANB8CsEB]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-24 19:32:00)", + "OpencodeHint" + ], + [ + " [msg_a17b4dc4c001x19oFZANB8CsEB]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_win_col": -3, - "priority": 10, "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -27,14 +45,19 @@ 2, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -42,14 +65,19 @@ 3, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -57,14 +85,19 @@ 4, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -72,14 +105,19 @@ 5, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, - "virt_text_win_col": -3, + "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -87,25 +125,41 @@ 8, 0, { - "virt_text_repeat_linebreak": false, + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-24 19:32:01)", "OpencodeHint"], - [" [msg_a17b4e166001vCnLczdZXvqLL6]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-24 19:32:01)", + "OpencodeHint" + ], + [ + " [msg_a17b4e166001vCnLczdZXvqLL6]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_win_col": -3, - "priority": 10, "virt_text_hide": false, - "ns_id": 3, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [], "lines": [ "----", "", @@ -425,5 +479,5 @@ "", "" ], - "timestamp": 1769091196 -} + "timestamp": 1770748576 +} \ No newline at end of file diff --git a/tests/data/permission-ask-new-approve.expected.json b/tests/data/permission-ask-new-approve.expected.json index 49a56564..d7394599 100644 --- a/tests/data/permission-ask-new-approve.expected.json +++ b/tests/data/permission-ask-new-approve.expected.json @@ -1,28 +1,43 @@ { "actions": [ { - "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], - "type": "diff_revert_selected_file", + "args": [ + "5ba6e95774829e7f501299039b72b72dc32c6620" + ], "display_line": 36, - "text": "[R]evert file", "key": "R", - "range": { "from": 36, "to": 36 } + "range": { + "from": 36, + "to": 36 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" }, { - "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], - "type": "diff_revert_all", + "args": [ + "5ba6e95774829e7f501299039b72b72dc32c6620" + ], "display_line": 36, - "text": "Revert [A]ll", "key": "A", - "range": { "from": 36, "to": 36 } + "range": { + "from": 36, + "to": 36 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" }, { - "args": ["5ba6e95774829e7f501299039b72b72dc32c6620"], - "type": "diff_open", + "args": [ + "5ba6e95774829e7f501299039b72b72dc32c6620" + ], "display_line": 36, - "text": "[D]iff", "key": "D", - "range": { "from": 36, "to": 36 } + "range": { + "from": 36, + "to": 36 + }, + "text": "[D]iff", + "type": "diff_open" } ], "extmarks": [ @@ -31,21 +46,38 @@ 1, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_hide": false, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-05 14:07:54)", "OpencodeHint"], - [" [msg_b8e7c60a2001Kisjwk2mVB4dye]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-05 14:07:54)", + "OpencodeHint" + ], + [ + " [msg_b8e7c60a2001Kisjwk2mVB4dye]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -53,14 +85,19 @@ 2, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -68,14 +105,19 @@ 3, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -83,14 +125,19 @@ 4, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -98,14 +145,19 @@ 5, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -113,14 +165,19 @@ 6, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -128,14 +185,19 @@ 7, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -143,14 +205,19 @@ 8, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -158,21 +225,38 @@ 10, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_hide": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-05 14:07:54)", "OpencodeHint"], - [" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-05 14:07:54)", + "OpencodeHint" + ], + [ + " [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -180,14 +264,19 @@ 12, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -195,14 +284,19 @@ 13, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -210,14 +304,19 @@ 14, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -225,14 +324,19 @@ 15, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -240,14 +344,19 @@ 16, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -255,14 +364,19 @@ 17, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -270,14 +384,19 @@ 18, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -285,14 +404,19 @@ 19, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -300,14 +424,19 @@ 20, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -315,14 +444,19 @@ 21, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -330,14 +464,19 @@ 22, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -345,14 +484,19 @@ 23, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -360,14 +504,19 @@ 24, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -375,14 +524,19 @@ 25, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -390,14 +544,19 @@ 26, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -405,14 +564,19 @@ 27, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -420,14 +584,19 @@ 28, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -435,14 +604,19 @@ 29, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -450,14 +624,19 @@ 30, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -465,14 +644,19 @@ 31, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -480,14 +664,19 @@ 32, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -495,14 +684,19 @@ 33, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -510,14 +704,19 @@ 34, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -525,25 +724,41 @@ 39, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_hide": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-05 14:08:26)", "OpencodeHint"], - [" [msg_b8e7cde9e0013hRkUyt0X2yMil]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-05 14:08:26)", + "OpencodeHint" + ], + [ + " [msg_b8e7cde9e0013hRkUyt0X2yMil]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "timestamp": 1769091210, "lines": [ "----", "", @@ -600,5 +815,6 @@ "No files are currently staged for commit. Let me know if you want more details (like the diff), want to stage/commit, or need help with anything else!", "", "" - ] -} + ], + "timestamp": 1770748577 +} \ No newline at end of file diff --git a/tests/data/permission-ask-new-deny.expected.json b/tests/data/permission-ask-new-deny.expected.json index 7c54fba9..644f997a 100644 --- a/tests/data/permission-ask-new-deny.expected.json +++ b/tests/data/permission-ask-new-deny.expected.json @@ -1,4 +1,5 @@ { + "actions": [], "extmarks": [ [ 1, @@ -6,19 +7,36 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-05 14:09:08)", "OpencodeHint"], - [" [msg_b8e7d8222001ZAc4G1t5RfHzou]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-05 14:09:08)", + "OpencodeHint" + ], + [ + " [msg_b8e7d8222001ZAc4G1t5RfHzou]", + "OpencodeHint" + ] ], - "right_gravity": true, - "priority": 10, - "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col", "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -28,12 +46,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -3 } ], @@ -43,12 +66,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -3 } ], @@ -58,12 +86,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -3 } ], @@ -73,12 +106,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -3 } ], @@ -88,19 +126,36 @@ 0, { "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-05 14:09:08)", "OpencodeHint"], - [" [msg_b8e7d823f001HX1Rr9DYE7TYBf]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-05 14:09:08)", + "OpencodeHint" + ], + [ + " [msg_b8e7d823f001HX1Rr9DYE7TYBf]", + "OpencodeHint" + ] ], - "right_gravity": true, - "priority": 10, - "virt_text_repeat_linebreak": false, - "virt_text_pos": "win_col", "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -110,12 +165,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -125,12 +185,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -140,12 +205,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -155,12 +225,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -170,12 +245,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -185,12 +265,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -200,12 +285,17 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ], @@ -215,17 +305,21 @@ 0, { "ns_id": 3, - "virt_text": [["▌", "OpencodeToolBorder"]], - "right_gravity": true, "priority": 4096, - "virt_text_repeat_linebreak": true, - "virt_text_pos": "win_col", + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } ] ], - "timestamp": 1767622360, "lines": [ "----", "", @@ -248,5 +342,5 @@ "", "" ], - "actions": [] -} + "timestamp": 1770748577 +} \ No newline at end of file diff --git a/tests/data/permission-ask-new.expected.json b/tests/data/permission-ask-new.expected.json index 19325386..1c2520a8 100644 --- a/tests/data/permission-ask-new.expected.json +++ b/tests/data/permission-ask-new.expected.json @@ -1,25 +1,43 @@ { + "actions": [], "extmarks": [ [ 1, 1, 0, { - "virt_text_repeat_linebreak": false, + "ns_id": 3, + "priority": 10, "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-05 14:07:54)", "OpencodeHint"], - [" [msg_b8e7c60a2001Kisjwk2mVB4dye]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-05 14:07:54)", + "OpencodeHint" + ], + [ + " [msg_b8e7c60a2001Kisjwk2mVB4dye]", + "OpencodeHint" + ] ], - "ns_id": 3, - "virt_text_win_col": -3, - "priority": 10, "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -27,14 +45,19 @@ 2, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -42,14 +65,19 @@ 3, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -57,14 +85,19 @@ 4, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -72,14 +105,19 @@ 5, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -87,14 +125,19 @@ 6, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -102,14 +145,19 @@ 7, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -117,14 +165,19 @@ 8, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -132,21 +185,38 @@ 10, 0, { - "virt_text_repeat_linebreak": false, + "ns_id": 3, + "priority": 10, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-05 14:07:54)", "OpencodeHint"], - [" [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-05 14:07:54)", + "OpencodeHint" + ], + [ + " [msg_b8e7c60f1001aEWYlAaDRXQ4aJ]", + "OpencodeHint" + ] ], - "ns_id": 3, - "virt_text_win_col": -3, - "priority": 10, "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -154,14 +224,19 @@ 12, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -169,14 +244,19 @@ 13, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -184,14 +264,19 @@ 14, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -199,14 +284,19 @@ 15, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -214,14 +304,19 @@ 16, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -229,14 +324,19 @@ 17, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -244,37 +344,69 @@ 20, 0, { - "virt_text_repeat_linebreak": false, + "ns_id": 3, + "priority": 10, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleSystem"], - [" "], - ["SYSTEM", "OpencodeMessageRoleSystem"], - ["", "OpencodeHint"], - ["", "OpencodeHint"], - [" [permission-display-message]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleSystem" + ], + [ + " " + ], + [ + "SYSTEM", + "OpencodeMessageRoleSystem" + ], + [ + "", + "OpencodeHint" + ], + [ + "", + "OpencodeHint" + ], + [ + " [permission-display-message]", + "OpencodeHint" + ] ], - "ns_id": 3, - "virt_text_win_col": -3, - "priority": 10, "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ], + [ + 17, + 22, + 0, + { + "line_hl_group": "OpencodePermissionTitle", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [17, 22, 0, { "line_hl_group": "OpencodePermissionTitle", "right_gravity": true, "priority": 4096, "ns_id": 3 }], [ 18, 22, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -282,14 +414,19 @@ 23, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -297,14 +434,19 @@ 24, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -312,30 +454,50 @@ 25, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 + } + ], + [ + 22, + 26, + 0, + { + "line_hl_group": "OpencodeDialogOptionHover", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [22, 26, 0, { "line_hl_group": "OpencodeDialogOptionHover", "right_gravity": true, "priority": 4096, "ns_id": 3 }], [ 23, 26, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -343,13 +505,18 @@ 26, 2, { - "virt_text_repeat_linebreak": false, - "right_gravity": true, - "virt_text": [["› ", "OpencodeDialogOptionHover"]], "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "› ", + "OpencodeDialogOptionHover" + ] + ], "virt_text_hide": false, - "virt_text_pos": "overlay" + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -357,14 +524,19 @@ 27, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -372,14 +544,19 @@ 28, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -387,14 +564,19 @@ 29, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ], [ @@ -402,18 +584,22 @@ 30, 0, { - "virt_text_repeat_linebreak": true, - "right_gravity": true, - "virt_text": [["▌", "OpencodePermissionBorder"]], "ns_id": 3, - "virt_text_win_col": -2, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text_pos": "win_col" + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -2 } ] ], - "actions": [], "lines": [ "----", "", @@ -449,5 +635,5 @@ "", "" ], - "timestamp": 1769449848 -} + "timestamp": 1770748577 +} \ No newline at end of file diff --git a/tests/data/permission-prompt.expected.json b/tests/data/permission-prompt.expected.json index 46b91edd..88b0001f 100644 --- a/tests/data/permission-prompt.expected.json +++ b/tests/data/permission-prompt.expected.json @@ -1,51 +1,41 @@ { - "lines": [ - "----", - "", - "", - "Perfect! Now I understand how it works. The message headers have extmarks with `virt_text` where the first element contains the icon (either `header_user` or `header_assistant`). Let me check the output_window module to understand the extmark namespace:", - "", - "** run** `Find extmark namespace usage`", - "", - "`````bash", - "> rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2", - "", - "`````", - "", - "----", - "", - "", - " Permission Required", - "", - " *bash* `rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2`", - "", - " 1. Allow once ", - " 2. Reject", - " 3. Allow always", - "", - "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", - "", - "" - ], + "actions": [], "extmarks": [ [ 1, 1, 0, { - "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-16 04:27:36)", "OpencodeHint"], - [" [msg_9eb45fbe60020xE560OGH3Vdoo]", "OpencodeHint"] - ], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 10, "right_gravity": true, + "virt_text": [ + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-16 04:27:36)", + "OpencodeHint" + ], + [ + " [msg_9eb45fbe60020xE560OGH3Vdoo]", + "OpencodeHint" + ] + ], "virt_text_hide": false, - "priority": 10, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } @@ -55,12 +45,17 @@ 5, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -70,12 +65,17 @@ 6, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -85,12 +85,17 @@ 7, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -100,12 +105,17 @@ 8, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -115,12 +125,17 @@ 9, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -130,12 +145,17 @@ 10, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -1 } @@ -145,35 +165,67 @@ 13, 0, { - "virt_text": [ - [" ", "OpencodeMessageRoleSystem"], - [" "], - ["SYSTEM", "OpencodeMessageRoleSystem"], - ["", "OpencodeHint"], - ["", "OpencodeHint"], - [" [permission-display-message]", "OpencodeHint"] - ], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 10, "right_gravity": true, + "virt_text": [ + [ + " ", + "OpencodeMessageRoleSystem" + ], + [ + " " + ], + [ + "SYSTEM", + "OpencodeMessageRoleSystem" + ], + [ + "", + "OpencodeHint" + ], + [ + "", + "OpencodeHint" + ], + [ + " [permission-display-message]", + "OpencodeHint" + ] + ], "virt_text_hide": false, - "priority": 10, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], - [9, 15, 0, { "priority": 4096, "line_hl_group": "OpencodePermissionTitle", "ns_id": 3, "right_gravity": true }], + [ + 9, + 15, + 0, + { + "line_hl_group": "OpencodePermissionTitle", + "ns_id": 3, + "priority": 4096, + "right_gravity": true + } + ], [ 10, 15, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -183,12 +235,17 @@ 16, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -198,12 +255,17 @@ 17, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -213,28 +275,48 @@ 18, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } ], - [14, 19, 0, { "priority": 4096, "line_hl_group": "OpencodeDialogOptionHover", "ns_id": 3, "right_gravity": true }], + [ + 14, + 19, + 0, + { + "line_hl_group": "OpencodeDialogOptionHover", + "ns_id": 3, + "priority": 4096, + "right_gravity": true + } + ], [ 15, 19, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -244,13 +326,18 @@ 19, 2, { - "virt_text": [["› ", "OpencodeDialogOptionHover"]], - "virt_text_pos": "overlay", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "› ", + "OpencodeDialogOptionHover" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": false, - "priority": 4096 + "virt_text_pos": "overlay", + "virt_text_repeat_linebreak": false } ], [ @@ -258,12 +345,17 @@ 20, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -273,12 +365,17 @@ 21, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -288,12 +385,17 @@ 22, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } @@ -303,17 +405,49 @@ 23, 0, { - "virt_text": [["▌", "OpencodePermissionBorder"]], - "virt_text_pos": "win_col", "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "priority": 4096, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, "virt_text_win_col": -2 } ] ], - "timestamp": 1769449859, - "actions": [] -} + "lines": [ + "----", + "", + "", + "Perfect! Now I understand how it works. The message headers have extmarks with `virt_text` where the first element contains the icon (either `header_user` or `header_assistant`). Let me check the output_window module to understand the extmark namespace:", + "", + "** run** `Find extmark namespace usage`", + "", + "`````bash", + "> rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2", + "", + "`````", + "", + "----", + "", + "", + " Permission Required", + "", + " *bash* `rg \"nvim_buf_get_extmarks|ns_id\" /Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/output_window.lua -B 2 -A 2`", + "", + " 1. Allow once ", + " 2. Reject", + " 3. Allow always", + "", + "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", + "", + "" + ], + "timestamp": 1770748578 +} \ No newline at end of file diff --git a/tests/data/permission.expected.json b/tests/data/permission.expected.json index 5155be7f..1e8833ed 100644 --- a/tests/data/permission.expected.json +++ b/tests/data/permission.expected.json @@ -1,4 +1,45 @@ { + "actions": [ + { + "args": [ + "c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d" + ], + "display_line": 14, + "key": "R", + "range": { + "from": 14, + "to": 14 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d" + ], + "display_line": 14, + "key": "A", + "range": { + "from": 14, + "to": 14 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d" + ], + "display_line": 14, + "key": "D", + "range": { + "from": 14, + "to": 14 + }, + "text": "[D]iff", + "type": "diff_open" + } + ], "extmarks": [ [ 1, @@ -7,19 +48,36 @@ { "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-12 05:43:49)", "OpencodeHint"], - [" [msg_9d6f253910015UFmkGkiWtUsRW]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-12 05:43:49)", + "OpencodeHint" + ], + [ + " [msg_9d6f253910015UFmkGkiWtUsRW]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -29,12 +87,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -44,12 +107,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -59,19 +127,36 @@ { "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-12 05:43:49)", "OpencodeHint"], - [" [msg_9d6f253df001TjqxW12FAjGf5s]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-12 05:43:49)", + "OpencodeHint" + ], + [ + " [msg_9d6f253df001TjqxW12FAjGf5s]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -81,12 +166,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -96,12 +186,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -111,12 +206,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -126,12 +226,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -141,12 +246,17 @@ { "ns_id": 3, "priority": 4096, - "virt_text_hide": false, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "virt_text_repeat_linebreak": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -156,48 +266,39 @@ { "ns_id": 3, "priority": 10, - "virt_text_hide": false, "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-12 05:44:00)", "OpencodeHint"], - [" [msg_9d6f27f4800103Tp3N6i6JW53p]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-12 05:44:00)", + "OpencodeHint" + ], + [ + " [msg_9d6f27f4800103Tp3N6i6JW53p]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "actions": [ - { - "key": "R", - "type": "diff_revert_selected_file", - "text": "[R]evert file", - "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], - "range": { "from": 14, "to": 14 }, - "display_line": 14 - }, - { - "key": "A", - "type": "diff_revert_all", - "text": "Revert [A]ll", - "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], - "range": { "from": 14, "to": 14 }, - "display_line": 14 - }, - { - "key": "D", - "type": "diff_open", - "text": "[D]iff", - "args": ["c78fb2dd2d533cfe530692cc3e3c8f92a0e4af1d"], - "range": { "from": 14, "to": 14 }, - "display_line": 14 - } - ], "lines": [ "----", "", @@ -220,5 +321,5 @@ "", "" ], - "timestamp": 1769172178 -} + "timestamp": 1770748578 +} \ No newline at end of file diff --git a/tests/data/planning.expected.json b/tests/data/planning.expected.json index b65135cc..63300cef 100644 --- a/tests/data/planning.expected.json +++ b/tests/data/planning.expected.json @@ -1,24 +1,42 @@ { + "actions": [], "extmarks": [ [ 1, 1, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": false, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-11 17:41:45)", "OpencodeHint"], - [" [msg_9d45d40c9001s7A1sP3Ew537QN]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-11 17:41:45)", + "OpencodeHint" + ], + [ + " [msg_9d45d40c9001s7A1sP3Ew537QN]", + "OpencodeHint" + ] ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -27,13 +45,18 @@ 2, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "virt_text_win_col": -3 } ], @@ -42,13 +65,18 @@ 3, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "virt_text_win_col": -3 } ], @@ -57,13 +85,18 @@ 4, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "virt_text_win_col": -3 } ], @@ -72,13 +105,18 @@ 5, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_hide": false, "virt_text_win_col": -3 } ], @@ -87,20 +125,37 @@ 8, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-11 17:41:45)", "OpencodeHint"], - [" [msg_9d45d411b00254Lm5jVRwAeQxT]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-11 17:41:45)", + "OpencodeHint" + ], + [ + " [msg_9d45d411b00254Lm5jVRwAeQxT]", + "OpencodeHint" + ] ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -109,13 +164,18 @@ 12, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -124,13 +184,18 @@ 13, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -139,13 +204,18 @@ 14, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -154,13 +224,18 @@ 15, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -169,13 +244,18 @@ 16, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -184,20 +264,37 @@ 19, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-11 17:41:51)", "OpencodeHint"], - [" [msg_9d45d585800269UgJnOLD8i2pF]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-11 17:41:51)", + "OpencodeHint" + ], + [ + " [msg_9d45d585800269UgJnOLD8i2pF]", + "OpencodeHint" + ] ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -206,20 +303,37 @@ 24, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-11 17:41:54)", "OpencodeHint"], - [" [msg_9d45d65b40026mDvwR5cCGTA30]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-11 17:41:54)", + "OpencodeHint" + ], + [ + " [msg_9d45d65b40026mDvwR5cCGTA30]", + "OpencodeHint" + ] ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ], @@ -228,13 +342,18 @@ 26, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -243,13 +362,18 @@ 27, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -258,13 +382,18 @@ 28, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -273,13 +402,18 @@ 29, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -288,13 +422,18 @@ 30, 0, { - "right_gravity": true, "ns_id": 3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], - "virt_text_hide": false, "virt_text_win_col": -1 } ], @@ -303,26 +442,41 @@ 33, 0, { - "right_gravity": true, "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", - "virt_text_repeat_linebreak": false, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-11 17:41:58)", "OpencodeHint"], - [" [msg_9d45d7390002yE2ve5szXtMdw0]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-11 17:41:58)", + "OpencodeHint" + ], + [ + " [msg_9d45d7390002yE2ve5szXtMdw0]", + "OpencodeHint" + ] ], "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, "virt_text_win_col": -3 } ] ], - "timestamp": 1769172200, - "actions": [], "lines": [ "----", "", @@ -368,5 +522,6 @@ "Once you provide these details, I can create a complete plugin structure for you based on the pattern I see in your existing example-plugin.", "", "" - ] -} + ], + "timestamp": 1770748578 +} \ No newline at end of file diff --git a/tests/data/question-ask-replied.expected.json b/tests/data/question-ask-replied.expected.json index a6469fc6..2b615069 100644 --- a/tests/data/question-ask-replied.expected.json +++ b/tests/data/question-ask-replied.expected.json @@ -1,25 +1,43 @@ { + "actions": [], "extmarks": [ [ 1, 1, 0, { + "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-26 14:23:12)", "OpencodeHint"], - [" [msg_bfaafef41001700DjtCwvhFxse]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-26 14:23:12)", + "OpencodeHint" + ], + [ + " [msg_bfaafef41001700DjtCwvhFxse]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -27,14 +45,19 @@ 2, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -42,14 +65,19 @@ 3, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -57,14 +85,19 @@ 4, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -72,14 +105,19 @@ 5, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -87,14 +125,19 @@ 6, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -102,21 +145,38 @@ 8, 0, { + "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:23:12)", "OpencodeHint"], - [" [msg_bfaafefb3001iBSmqfzFffDjCS]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:23:12)", + "OpencodeHint" + ], + [ + " [msg_bfaafefb3001iBSmqfzFffDjCS]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -124,14 +184,19 @@ 10, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -139,14 +204,19 @@ 11, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -154,14 +224,19 @@ 12, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -169,14 +244,19 @@ 13, 0, { - "priority": 4096, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -184,25 +264,41 @@ 16, 0, { + "ns_id": 3, "priority": 10, - "virt_text_pos": "win_col", + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:23:22)", "OpencodeHint"], - [" [msg_bfab0156d001NyMxaTnj6Cml0c]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:23:22)", + "OpencodeHint" + ], + [ + " [msg_bfab0156d001NyMxaTnj6Cml0c]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] ], - "timestamp": 1769437626, "lines": [ "----", "", @@ -226,5 +322,5 @@ "", "" ], - "actions": [] -} + "timestamp": 1770748578 +} \ No newline at end of file diff --git a/tests/data/question-ask.expected.json b/tests/data/question-ask.expected.json index 20c0e980..03f33a89 100644 --- a/tests/data/question-ask.expected.json +++ b/tests/data/question-ask.expected.json @@ -6,21 +6,38 @@ 1, 0, { + "ns_id": 3, + "priority": 10, "right_gravity": true, - "virt_text_win_col": -3, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2026-01-26 14:06:19)", "OpencodeHint"], - [" [msg_bfaa078a2001RUYhlKZhSlyWeF]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2026-01-26 14:06:19)", + "OpencodeHint" + ], + [ + " [msg_bfaa078a2001RUYhlKZhSlyWeF]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -28,14 +45,19 @@ 2, 0, { - "right_gravity": true, - "virt_text_win_col": -3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -43,14 +65,19 @@ 3, 0, { - "right_gravity": true, - "virt_text_win_col": -3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -58,14 +85,19 @@ 4, 0, { - "right_gravity": true, - "virt_text_win_col": -3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -73,14 +105,19 @@ 5, 0, { - "right_gravity": true, - "virt_text_win_col": -3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -88,14 +125,19 @@ 6, 0, { - "right_gravity": true, - "virt_text_win_col": -3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -103,21 +145,38 @@ 8, 0, { + "ns_id": 3, + "priority": 10, "right_gravity": true, - "virt_text_win_col": -3, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2026-01-26 14:06:19)", "OpencodeHint"], - [" [msg_bfaa078e7001qc33BqBlqzHv8X]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2026-01-26 14:06:19)", + "OpencodeHint" + ], + [ + " [msg_bfaa078e7001qc33BqBlqzHv8X]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -125,37 +184,69 @@ 13, 0, { + "ns_id": 3, + "priority": 10, "right_gravity": true, - "virt_text_win_col": -3, "virt_text": [ - [" ", "OpencodeMessageRoleSystem"], - [" "], - ["SYSTEM", "OpencodeMessageRoleSystem"], - ["", "OpencodeHint"], - ["", "OpencodeHint"], - [" [question-display-message]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleSystem" + ], + [ + " " + ], + [ + "SYSTEM", + "OpencodeMessageRoleSystem" + ], + [ + "", + "OpencodeHint" + ], + [ + "", + "OpencodeHint" + ], + [ + " [question-display-message]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 + } + ], + [ + 9, + 15, + 0, + { + "line_hl_group": "OpencodeQuestionTitle", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [9, 15, 0, { "right_gravity": true, "priority": 4096, "line_hl_group": "OpencodeQuestionTitle", "ns_id": 3 }], [ 10, 15, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -163,14 +254,19 @@ 16, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -178,14 +274,19 @@ 17, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -193,30 +294,50 @@ 18, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 + } + ], + [ + 14, + 19, + 0, + { + "line_hl_group": "OpencodeDialogOptionHover", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [14, 19, 0, { "right_gravity": true, "priority": 4096, "line_hl_group": "OpencodeDialogOptionHover", "ns_id": 3 }], [ 15, 19, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -224,13 +345,18 @@ 19, 2, { - "virt_text": [["› ", "OpencodeDialogOptionHover"]], - "virt_text_hide": false, "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "› ", + "OpencodeDialogOptionHover" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 4096 + "virt_text_repeat_linebreak": false } ], [ @@ -238,14 +364,19 @@ 20, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -253,14 +384,19 @@ 21, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -268,14 +404,19 @@ 22, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -283,14 +424,19 @@ 23, 0, { - "right_gravity": true, - "virt_text_win_col": -2, - "virt_text": [["▌", "OpencodeQuestionBorder"]], "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeQuestionBorder" + ] + ], "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ] ], @@ -322,5 +468,5 @@ "", "" ], - "timestamp": 1769441972 -} + "timestamp": 1770748579 +} \ No newline at end of file diff --git a/tests/data/reasoning.expected.json b/tests/data/reasoning.expected.json index 178a92e8..75c4f6ce 100644 --- a/tests/data/reasoning.expected.json +++ b/tests/data/reasoning.expected.json @@ -1,27 +1,43 @@ { "actions": [], - "timestamp": 1766432126, "extmarks": [ [ 1, 1, 0, { - "virt_text_hide": false, - "virt_text_pos": "win_col", - "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-12-17 19:33:20)", "OpencodeHint"], - [" [msg_reason_user1]", "OpencodeHint"] - ], "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, + "virt_text": [ + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-12-17 19:33:20)", + "OpencodeHint" + ], + [ + " [msg_reason_user1]", + "OpencodeHint" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -29,14 +45,19 @@ 2, 0, { - "virt_text_hide": false, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -44,14 +65,19 @@ 3, 0, { - "virt_text_hide": false, - "virt_text_pos": "win_col", - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -59,21 +85,38 @@ 6, 0, { - "virt_text_hide": false, - "virt_text_pos": "win_col", - "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-12-17 19:33:20)", "OpencodeHint"], - [" [msg_reason_asst1]", "OpencodeHint"] - ], "ns_id": 3, - "virt_text_win_col": -3, "priority": 10, + "right_gravity": true, + "virt_text": [ + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-12-17 19:33:20)", + "OpencodeHint" + ], + [ + " [msg_reason_asst1]", + "OpencodeHint" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -81,15 +124,20 @@ 8, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -97,15 +145,20 @@ 9, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -113,15 +166,20 @@ 10, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -129,15 +187,20 @@ 11, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -145,15 +208,20 @@ 12, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -161,15 +229,20 @@ 13, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -177,15 +250,20 @@ 14, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -193,15 +271,20 @@ 15, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -209,15 +292,20 @@ 16, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -225,15 +313,20 @@ 17, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -241,15 +334,20 @@ 18, 0, { - "virt_text_hide": false, - "virt_text_repeat_linebreak": true, + "line_hl_group": "OpencodeReasoningText", "ns_id": 3, + "priority": 4096, "right_gravity": true, - "virt_text": [["▌", "OpencodeToolBorder"]], + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "line_hl_group": "OpencodeReasoningText" + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ] ], @@ -286,5 +384,6 @@ "This title clearly identifies what the tool is (OpenCode), the platform it works with (Neovim), and its primary function (AI-powered coding assistance).", "", "" - ] -} + ], + "timestamp": 1770748579 +} \ No newline at end of file diff --git a/tests/data/redo-all.expected.json b/tests/data/redo-all.expected.json index 7996a65d..58dd7d81 100644 --- a/tests/data/redo-all.expected.json +++ b/tests/data/redo-all.expected.json @@ -1,25 +1,161 @@ { + "actions": [ + { + "args": [ + "d988cc85565b99017d40ad8baea20225165be9d5" + ], + "display_line": 90, + "key": "R", + "range": { + "from": 90, + "to": 90 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "d988cc85565b99017d40ad8baea20225165be9d5" + ], + "display_line": 90, + "key": "A", + "range": { + "from": 90, + "to": 90 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "d988cc85565b99017d40ad8baea20225165be9d5" + ], + "display_line": 90, + "key": "D", + "range": { + "from": 90, + "to": 90 + }, + "text": "[D]iff", + "type": "diff_open" + }, + { + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], + "display_line": 22, + "key": "R", + "range": { + "from": 22, + "to": 22 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], + "display_line": 22, + "key": "A", + "range": { + "from": 22, + "to": 22 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], + "display_line": 22, + "key": "D", + "range": { + "from": 22, + "to": 22 + }, + "text": "[D]iff", + "type": "diff_open" + }, + { + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], + "display_line": 56, + "key": "R", + "range": { + "from": 56, + "to": 56 + }, + "text": "[R]evert file", + "type": "diff_revert_selected_file" + }, + { + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], + "display_line": 56, + "key": "A", + "range": { + "from": 56, + "to": 56 + }, + "text": "Revert [A]ll", + "type": "diff_revert_all" + }, + { + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], + "display_line": 56, + "key": "D", + "range": { + "from": 56, + "to": 56 + }, + "text": "[D]iff", + "type": "diff_open" + } + ], "extmarks": [ [ 1, 1, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 15:20:02)", "OpencodeHint"], - [" [msg_a0234c0b7001y2o9S1jMaNVZar]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:02)", + "OpencodeHint" + ], + [ + " [msg_a0234c0b7001y2o9S1jMaNVZar]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -27,14 +163,19 @@ 2, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -42,14 +183,19 @@ 3, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -57,14 +203,19 @@ 4, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -72,14 +223,19 @@ 5, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -87,21 +243,38 @@ 8, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:04)", "OpencodeHint"], - [" [msg_a0234c7960011LTxTvD94hfWCi]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:04)", + "OpencodeHint" + ], + [ + " [msg_a0234c7960011LTxTvD94hfWCi]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -109,14 +282,19 @@ 12, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -124,14 +302,19 @@ 13, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -139,14 +322,19 @@ 14, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -154,18 +342,23 @@ 15, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffDelete", - "hl_eol": true, - "virt_text": [["-", "OpencodeDiffDelete"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 16, + "hl_eol": true, + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 16, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -173,14 +366,19 @@ 15, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -188,18 +386,23 @@ 16, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffAdd", - "hl_eol": true, - "virt_text": [["+", "OpencodeDiffAdd"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 17, + "hl_eol": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 17, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -207,14 +410,19 @@ 16, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -222,14 +430,19 @@ 17, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -237,14 +450,19 @@ 18, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -252,14 +470,19 @@ 19, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -267,14 +490,19 @@ 20, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -282,21 +510,38 @@ 25, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:09)", "OpencodeHint"], - [" [msg_a0234d8fb001SXyngLjuKSuxOY]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:09)", + "OpencodeHint" + ], + [ + " [msg_a0234d8fb001SXyngLjuKSuxOY]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -304,21 +549,38 @@ 30, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 15:20:11)", "OpencodeHint"], - [" [msg_a0234e308001SKl5bQUibp5gtI]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:11)", + "OpencodeHint" + ], + [ + " [msg_a0234e308001SKl5bQUibp5gtI]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -326,14 +588,19 @@ 31, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -341,14 +608,19 @@ 32, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -356,21 +628,38 @@ 35, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:11)", "OpencodeHint"], - [" [msg_a0234e31f001m4EsQdPmY3PTtS]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:11)", + "OpencodeHint" + ], + [ + " [msg_a0234e31f001m4EsQdPmY3PTtS]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -378,21 +667,38 @@ 42, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:16)", "OpencodeHint"], - [" [msg_a0234f482001PQbMjWc6W8s0eF]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:16)", + "OpencodeHint" + ], + [ + " [msg_a0234f482001PQbMjWc6W8s0eF]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -400,14 +706,19 @@ 46, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -415,14 +726,19 @@ 47, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -430,14 +746,19 @@ 48, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -445,18 +766,23 @@ 49, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffDelete", - "hl_eol": true, - "virt_text": [["-", "OpencodeDiffDelete"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 50, + "hl_eol": true, + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 50, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -464,14 +790,19 @@ 49, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -479,18 +810,23 @@ 50, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffAdd", - "hl_eol": true, - "virt_text": [["+", "OpencodeDiffAdd"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 51, + "hl_eol": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 51, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -498,14 +834,19 @@ 50, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -513,14 +854,19 @@ 51, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -528,14 +874,19 @@ 52, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -543,14 +894,19 @@ 53, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -558,14 +914,19 @@ 54, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -573,21 +934,38 @@ 59, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:17)", "OpencodeHint"], - [" [msg_a0234f9c6001JCKYaca1HHwwx6]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:17)", + "OpencodeHint" + ], + [ + " [msg_a0234f9c6001JCKYaca1HHwwx6]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -595,21 +973,38 @@ 64, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 15:22:29)", "OpencodeHint"], - [" [msg_a0236fd1c001TlwqL8fwvq529i]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 15:22:29)", + "OpencodeHint" + ], + [ + " [msg_a0236fd1c001TlwqL8fwvq529i]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -617,14 +1012,19 @@ 65, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -632,14 +1032,19 @@ 66, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -3, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -647,21 +1052,38 @@ 69, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:22:29)", "OpencodeHint"], - [" [msg_a0236fd57001pTnTjSBdFlleCb]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:22:29)", + "OpencodeHint" + ], + [ + " [msg_a0236fd57001pTnTjSBdFlleCb]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -669,21 +1091,38 @@ 76, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:22:34)", "OpencodeHint"], - [" [msg_a02371241001PBQAsr8Oc9hqNI]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:22:34)", + "OpencodeHint" + ], + [ + " [msg_a02371241001PBQAsr8Oc9hqNI]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ], [ @@ -691,14 +1130,19 @@ 80, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -706,14 +1150,19 @@ 81, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -721,14 +1170,19 @@ 82, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -736,18 +1190,23 @@ 83, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffDelete", - "hl_eol": true, - "virt_text": [["-", "OpencodeDiffDelete"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 84, + "hl_eol": true, + "hl_group": "OpencodeDiffDelete", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 84, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -755,14 +1214,19 @@ 83, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -770,18 +1234,23 @@ 84, 0, { - "ns_id": 3, "end_col": 0, - "hl_group": "OpencodeDiffAdd", - "hl_eol": true, - "virt_text": [["+", "OpencodeDiffAdd"]], "end_right_gravity": false, - "virt_text_hide": false, + "end_row": 85, + "hl_eol": true, + "hl_group": "OpencodeDiffAdd", + "ns_id": 3, "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, "virt_text_pos": "overlay", - "end_row": 85, - "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_repeat_linebreak": false } ], [ @@ -789,14 +1258,19 @@ 84, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -804,14 +1278,19 @@ 85, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -819,14 +1298,19 @@ 86, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -834,14 +1318,19 @@ 87, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -849,14 +1338,19 @@ 88, 0, { - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, - "virt_text_win_col": -1, "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "right_gravity": true + "virt_text_win_col": -1 } ], [ @@ -864,99 +1358,41 @@ 93, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:22:39)", "OpencodeHint"], - [" [msg_a023723d0001r87MaJThFssUw1]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:22:39)", + "OpencodeHint" + ], + [ + " [msg_a023723d0001r87MaJThFssUw1]", + "OpencodeHint" + ] ], - "ns_id": 3, "virt_text_hide": false, - "virt_text_win_col": -3, - "priority": 10, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "right_gravity": true + "virt_text_win_col": -3 } ] ], - "timestamp": 1770644135, - "actions": [ - { - "type": "diff_revert_selected_file", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], - "range": { "from": 22, "to": 22 }, - "key": "R", - "text": "[R]evert file", - "display_line": 22 - }, - { - "type": "diff_revert_all", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], - "range": { "from": 22, "to": 22 }, - "key": "A", - "text": "Revert [A]ll", - "display_line": 22 - }, - { - "type": "diff_open", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], - "range": { "from": 22, "to": 22 }, - "key": "D", - "text": "[D]iff", - "display_line": 22 - }, - { - "type": "diff_revert_selected_file", - "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], - "range": { "from": 90, "to": 90 }, - "key": "R", - "text": "[R]evert file", - "display_line": 90 - }, - { - "type": "diff_revert_all", - "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], - "range": { "from": 90, "to": 90 }, - "key": "A", - "text": "Revert [A]ll", - "display_line": 90 - }, - { - "type": "diff_open", - "args": ["d988cc85565b99017d40ad8baea20225165be9d5"], - "range": { "from": 90, "to": 90 }, - "key": "D", - "text": "[D]iff", - "display_line": 90 - }, - { - "type": "diff_revert_selected_file", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], - "range": { "from": 56, "to": 56 }, - "key": "R", - "text": "[R]evert file", - "display_line": 56 - }, - { - "type": "diff_revert_all", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], - "range": { "from": 56, "to": 56 }, - "key": "A", - "text": "Revert [A]ll", - "display_line": 56 - }, - { - "type": "diff_open", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], - "range": { "from": 56, "to": 56 }, - "key": "D", - "text": "[D]iff", - "display_line": 56 - } - ], "lines": [ "----", "", @@ -1056,5 +1492,6 @@ "**Done:** appended the word `again3` to `test.txt`.", "", "" - ] -} + ], + "timestamp": 1770748580 +} \ No newline at end of file diff --git a/tests/data/redo-once.expected.json b/tests/data/redo-once.expected.json index ed6d7bde..71f26d3f 100644 --- a/tests/data/redo-once.expected.json +++ b/tests/data/redo-once.expected.json @@ -1,51 +1,81 @@ { "actions": [ { - "text": "[R]evert file", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], "display_line": 56, "key": "R", - "range": { "to": 56, "from": 56 }, + "range": { + "from": 56, + "to": 56 + }, + "text": "[R]evert file", "type": "diff_revert_selected_file" }, { - "text": "Revert [A]ll", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], "display_line": 56, "key": "A", - "range": { "to": 56, "from": 56 }, + "range": { + "from": 56, + "to": 56 + }, + "text": "Revert [A]ll", "type": "diff_revert_all" }, { - "text": "[D]iff", - "args": ["57d83f5596cb1f142fbc681d3d93b7184f7f73cd"], + "args": [ + "57d83f5596cb1f142fbc681d3d93b7184f7f73cd" + ], "display_line": 56, "key": "D", - "range": { "to": 56, "from": 56 }, + "range": { + "from": 56, + "to": 56 + }, + "text": "[D]iff", "type": "diff_open" }, { - "text": "[R]evert file", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], "display_line": 22, "key": "R", - "range": { "to": 22, "from": 22 }, + "range": { + "from": 22, + "to": 22 + }, + "text": "[R]evert file", "type": "diff_revert_selected_file" }, { - "text": "Revert [A]ll", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], "display_line": 22, "key": "A", - "range": { "to": 22, "from": 22 }, + "range": { + "from": 22, + "to": 22 + }, + "text": "Revert [A]ll", "type": "diff_revert_all" }, { - "text": "[D]iff", - "args": ["1b6ba655c6c0d899965adff278ac6320d5fc3b12"], + "args": [ + "1b6ba655c6c0d899965adff278ac6320d5fc3b12" + ], "display_line": 22, "key": "D", - "range": { "to": 22, "from": 22 }, + "range": { + "from": 22, + "to": 22 + }, + "text": "[D]iff", "type": "diff_open" } ], @@ -55,21 +85,38 @@ 1, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 15:20:02)", "OpencodeHint"], - [" [msg_a0234c0b7001y2o9S1jMaNVZar]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:02)", + "OpencodeHint" + ], + [ + " [msg_a0234c0b7001y2o9S1jMaNVZar]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -77,14 +124,19 @@ 2, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -92,14 +144,19 @@ 3, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -107,14 +164,19 @@ 4, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -122,14 +184,19 @@ 5, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -137,21 +204,38 @@ 8, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:04)", "OpencodeHint"], - [" [msg_a0234c7960011LTxTvD94hfWCi]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:04)", + "OpencodeHint" + ], + [ + " [msg_a0234c7960011LTxTvD94hfWCi]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -159,14 +243,19 @@ 12, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -174,14 +263,19 @@ 13, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -189,14 +283,19 @@ 14, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -205,16 +304,21 @@ 0, { "end_col": 0, + "end_right_gravity": false, "end_row": 16, - "virt_text_pos": "overlay", - "virt_text_hide": false, + "hl_eol": true, "hl_group": "OpencodeDiffDelete", - "priority": 5000, "ns_id": 3, - "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["-", "OpencodeDiffDelete"]], + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -223,14 +327,19 @@ 15, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -239,16 +348,21 @@ 0, { "end_col": 0, + "end_right_gravity": false, "end_row": 17, - "virt_text_pos": "overlay", - "virt_text_hide": false, + "hl_eol": true, "hl_group": "OpencodeDiffAdd", - "priority": 5000, "ns_id": 3, - "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -257,14 +371,19 @@ 16, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -272,14 +391,19 @@ 17, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -287,14 +411,19 @@ 18, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -302,14 +431,19 @@ 19, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -317,14 +451,19 @@ 20, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -332,21 +471,38 @@ 25, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:09)", "OpencodeHint"], - [" [msg_a0234d8fb001SXyngLjuKSuxOY]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:09)", + "OpencodeHint" + ], + [ + " [msg_a0234d8fb001SXyngLjuKSuxOY]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -354,21 +510,38 @@ 30, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-20 15:20:11)", "OpencodeHint"], - [" [msg_a0234e308001SKl5bQUibp5gtI]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:11)", + "OpencodeHint" + ], + [ + " [msg_a0234e308001SKl5bQUibp5gtI]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -376,14 +549,19 @@ 31, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -391,14 +569,19 @@ 32, 0, { - "priority": 4096, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -406,21 +589,38 @@ 35, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:11)", "OpencodeHint"], - [" [msg_a0234e31f001m4EsQdPmY3PTtS]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:11)", + "OpencodeHint" + ], + [ + " [msg_a0234e31f001m4EsQdPmY3PTtS]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -428,21 +628,38 @@ 42, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:16)", "OpencodeHint"], - [" [msg_a0234f482001PQbMjWc6W8s0eF]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:16)", + "OpencodeHint" + ], + [ + " [msg_a0234f482001PQbMjWc6W8s0eF]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -450,14 +667,19 @@ 46, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -465,14 +687,19 @@ 47, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -480,14 +707,19 @@ 48, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -496,16 +728,21 @@ 0, { "end_col": 0, + "end_right_gravity": false, "end_row": 50, - "virt_text_pos": "overlay", - "virt_text_hide": false, + "hl_eol": true, "hl_group": "OpencodeDiffDelete", - "priority": 5000, "ns_id": 3, - "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["-", "OpencodeDiffDelete"]], + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "-", + "OpencodeDiffDelete" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -514,14 +751,19 @@ 49, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -530,16 +772,21 @@ 0, { "end_col": 0, + "end_right_gravity": false, "end_row": 51, - "virt_text_pos": "overlay", - "virt_text_hide": false, + "hl_eol": true, "hl_group": "OpencodeDiffAdd", - "priority": 5000, "ns_id": 3, - "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], + "priority": 5000, "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "overlay", "virt_text_repeat_linebreak": false } ], @@ -548,14 +795,19 @@ 50, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -563,14 +815,19 @@ 51, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -578,14 +835,19 @@ 52, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -593,14 +855,19 @@ 53, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -608,14 +875,19 @@ 54, 0, { - "priority": 4096, - "virt_text_win_col": -1, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["▌", "OpencodeToolBorder"]], + "priority": 4096, "right_gravity": true, - "virt_text_repeat_linebreak": true + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -623,21 +895,38 @@ 59, 0, { - "priority": 10, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-5-mini", "OpencodeHint"], - [" (2025-10-20 15:20:17)", "OpencodeHint"], - [" [msg_a0234f9c6001JCKYaca1HHwwx6]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-5-mini", + "OpencodeHint" + ], + [ + " (2025-10-20 15:20:17)", + "OpencodeHint" + ], + [ + " [msg_a0234f9c6001JCKYaca1HHwwx6]", + "OpencodeHint" + ] ], - "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -645,14 +934,19 @@ 69, 0, { - "priority": 1000, - "virt_text_win_col": 12, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["+1", "OpencodeDiffAddText"]], + "priority": 1000, "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text": [ + [ + "+1", + "OpencodeDiffAddText" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": 12 } ], [ @@ -660,14 +954,19 @@ 69, 0, { - "priority": 1000, - "virt_text_win_col": 15, "ns_id": 3, - "virt_text_pos": "win_col", - "virt_text_hide": false, - "virt_text": [["-1", "OpencodeDiffDeleteText"]], + "priority": 1000, "right_gravity": true, - "virt_text_repeat_linebreak": false + "virt_text": [ + [ + "-1", + "OpencodeDiffDeleteText" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": 15 } ] ], @@ -744,5 +1043,5 @@ " test.txt: +1 -1", "" ], - "timestamp": 1769172261 -} + "timestamp": 1770748580 +} \ No newline at end of file diff --git a/tests/data/revert.expected.json b/tests/data/revert.expected.json index 5c0415e9..c5df5321 100644 --- a/tests/data/revert.expected.json +++ b/tests/data/revert.expected.json @@ -1,97 +1,42 @@ { - "lines": [ - "----", - "", - "", - "write 10 random words", - "", - "[`poem.md`](poem.md)", - "", - "----", - "", - "", - "Here are 10 random words:", - "", - "1. Lantern ", - "2. Whisper ", - "3. Velvet ", - "4. Orbit ", - "5. Timber ", - "6. Quiver ", - "7. Mosaic ", - "8. Ember ", - "9. Spiral ", - "10. Glimmer", - "", - "Let me know if you need them in a specific format or want to use them in a file!", - "", - "----", - "", - "", - "write 10 random words to the file", - "", - "----", - "", - "", - "I will write 10 random words to poem.md, each on a new line.", - "", - "Proceeding to update the file now.", - "", - "** write** `/home/francis/Projects/_nvim/opencode.nvim/poem.md`", - "", - "`````markdown", - "Lantern", - "Whisper", - "Velvet", - "Orbit", - "Timber", - "Quiver", - "Mosaic", - "Ember", - "Spiral", - "Glimmer", - "", - "`````", - "", - "**󰻛 Created Snapshot** `c410b2b4`", - "", - "----", - "", - "", - "The file poem.md has been updated with 10 random words, each on a new line. Task complete! If you need anything else, let me know.", - "", - "----", - "", - "> 2 messages reverted, 4 tool calls reverted", - ">", - "> type `/redo` to restore.", - "", - " poem.md: -20", - "" - ], "actions": [ { - "text": "[R]evert file", + "args": [ + "c410b2b4024de020aea223c5248eec89216de53f" + ], "display_line": 53, "key": "R", - "range": { "to": 53, "from": 53 }, - "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "range": { + "from": 53, + "to": 53 + }, + "text": "[R]evert file", "type": "diff_revert_selected_file" }, { - "text": "Revert [A]ll", + "args": [ + "c410b2b4024de020aea223c5248eec89216de53f" + ], "display_line": 53, "key": "A", - "range": { "to": 53, "from": 53 }, - "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "range": { + "from": 53, + "to": 53 + }, + "text": "Revert [A]ll", "type": "diff_revert_all" }, { - "text": "[D]iff", + "args": [ + "c410b2b4024de020aea223c5248eec89216de53f" + ], "display_line": 53, "key": "D", - "range": { "to": 53, "from": 53 }, - "args": ["c410b2b4024de020aea223c5248eec89216de53f"], + "range": { + "from": 53, + "to": 53 + }, + "text": "[D]iff", "type": "diff_open" } ], @@ -102,20 +47,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, - "virt_text_repeat_linebreak": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-19 17:50:43)", "OpencodeHint"], - [" [msg_9fd985573001fk1Xlot7uyDgTo]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-19 17:50:43)", + "OpencodeHint" + ], + [ + " [msg_9fd985573001fk1Xlot7uyDgTo]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -124,13 +86,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -139,13 +106,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -154,13 +126,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -169,13 +146,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -184,20 +166,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, - "virt_text_repeat_linebreak": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-10-19 17:50:44)", "OpencodeHint"], - [" [msg_9fd985a4d001wOX3Op7CpFiCTq]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-10-19 17:50:44)", + "OpencodeHint" + ], + [ + " [msg_9fd985a4d001wOX3Op7CpFiCTq]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -206,20 +205,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, - "virt_text_repeat_linebreak": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-19 17:50:57)", "OpencodeHint"], - [" [msg_9fd988c92001w0IZCVPQsN6xa9]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-19 17:50:57)", + "OpencodeHint" + ], + [ + " [msg_9fd988c92001w0IZCVPQsN6xa9]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -228,13 +244,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -243,13 +264,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -258,20 +284,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, - "virt_text_repeat_linebreak": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-10-19 17:50:57)", "OpencodeHint"], - [" [msg_9fd988ca7001lgaGttpI4YeGSA]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-10-19 17:50:57)", + "OpencodeHint" + ], + [ + " [msg_9fd988ca7001lgaGttpI4YeGSA]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -280,13 +323,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -295,13 +343,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -310,13 +363,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -325,13 +383,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -340,13 +403,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -355,13 +423,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -370,13 +443,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -385,13 +463,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -400,13 +483,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -415,13 +503,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -430,13 +523,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -445,13 +543,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -460,13 +563,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -475,13 +583,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -490,13 +603,18 @@ 0, { "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "virt_text_pos": "win_col", - "virt_text_win_col": -1, - "priority": 4096, - "right_gravity": true + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -505,20 +623,37 @@ 0, { "ns_id": 3, - "virt_text_hide": false, - "virt_text_repeat_linebreak": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" gpt-4.1", "OpencodeHint"], - [" (2025-10-19 17:50:59)", "OpencodeHint"], - [" [msg_9fd98942d001elqd2sd8CZeOoA]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " gpt-4.1", + "OpencodeHint" + ], + [ + " (2025-10-19 17:50:59)", + "OpencodeHint" + ], + [ + " [msg_9fd98942d001elqd2sd8CZeOoA]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -527,15 +662,90 @@ 0, { "ns_id": 3, + "priority": 1000, + "right_gravity": true, + "virt_text": [ + [ + "-20", + "OpencodeDiffDeleteText" + ] + ], "virt_text_hide": false, - "virt_text_repeat_linebreak": false, - "virt_text": [["-20", "OpencodeDiffDeleteText"]], "virt_text_pos": "win_col", - "virt_text_win_col": 11, - "priority": 1000, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": 11 } ] ], - "timestamp": 1769172286 -} + "lines": [ + "----", + "", + "", + "write 10 random words", + "", + "[`poem.md`](poem.md)", + "", + "----", + "", + "", + "Here are 10 random words:", + "", + "1. Lantern ", + "2. Whisper ", + "3. Velvet ", + "4. Orbit ", + "5. Timber ", + "6. Quiver ", + "7. Mosaic ", + "8. Ember ", + "9. Spiral ", + "10. Glimmer", + "", + "Let me know if you need them in a specific format or want to use them in a file!", + "", + "----", + "", + "", + "write 10 random words to the file", + "", + "----", + "", + "", + "I will write 10 random words to poem.md, each on a new line.", + "", + "Proceeding to update the file now.", + "", + "** write** `/home/francis/Projects/_nvim/opencode.nvim/poem.md`", + "", + "`````markdown", + "Lantern", + "Whisper", + "Velvet", + "Orbit", + "Timber", + "Quiver", + "Mosaic", + "Ember", + "Spiral", + "Glimmer", + "", + "`````", + "", + "**󰻛 Created Snapshot** `c410b2b4`", + "", + "----", + "", + "", + "The file poem.md has been updated with 10 random words, each on a new line. Task complete! If you need anything else, let me know.", + "", + "----", + "", + "> 2 messages reverted, 4 tool calls reverted", + ">", + "> type `/redo` to restore.", + "", + " poem.md: -20", + "" + ], + "timestamp": 1770748581 +} \ No newline at end of file diff --git a/tests/data/selection.expected.json b/tests/data/selection.expected.json index f6d0e006..5b771198 100644 --- a/tests/data/selection.expected.json +++ b/tests/data/selection.expected.json @@ -1,56 +1,43 @@ { - "timestamp": 1766432128, "actions": [], - "lines": [ - "----", - "", - "", - "here's some context", - "", - "```txt", - "this is a string", - "```", - "", - "```txt", - "this is a selection test", - "```", - "", - "[`diff-test.txt`](diff-test.txt)", - "", - "----", - "", - "", - "I can see the file `/Users/cam/tmp/a/diff-test.txt` contains \"this is a string\" on line 1.", - "", - "The two selection contexts you provided show:", - "1. Current content: \"this is a string\"", - "2. Expected/desired content: \"this is a selection test\"", - "", - "Since I'm in read-only mode, I can only observe that there's a difference between what's currently in the file and what one of the selections indicates should be there.", - "", - "" - ], "extmarks": [ [ 1, 1, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-18 05:02:28)", "OpencodeHint"], - [" [msg_9f5b29fea001z6jYXF7CG9omHa]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-18 05:02:28)", + "OpencodeHint" + ], + [ + " [msg_9f5b29fea001z6jYXF7CG9omHa]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -58,14 +45,19 @@ 2, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -73,14 +65,19 @@ 3, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -88,14 +85,19 @@ 4, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -103,14 +105,19 @@ 5, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -118,14 +125,19 @@ 6, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -133,14 +145,19 @@ 7, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -148,14 +165,19 @@ 8, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -163,14 +185,19 @@ 8, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -178,14 +205,19 @@ 9, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -193,14 +225,19 @@ 10, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -208,14 +245,19 @@ 11, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -223,14 +265,19 @@ 12, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -238,14 +285,19 @@ 12, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -253,14 +305,19 @@ 13, 0, { - "virt_text_repeat_linebreak": true, - "virt_text_hide": false, "ns_id": 3, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "virt_text_pos": "win_col", - "virt_text_win_col": -3, "priority": 4096, - "right_gravity": true + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 } ], [ @@ -268,22 +325,69 @@ 16, 0, { - "virt_text_repeat_linebreak": false, - "virt_text_hide": false, "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-18 05:02:28)", "OpencodeHint"], - [" [msg_9f5b2a039001xop8ITmXQq0Gjh]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-18 05:02:28)", + "OpencodeHint" + ], + [ + " [msg_9f5b2a039001xop8ITmXQq0Gjh]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", - "virt_text_win_col": -3, - "priority": 10, - "right_gravity": true + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ] - ] -} + ], + "lines": [ + "----", + "", + "", + "here's some context", + "", + "```txt", + "this is a string", + "```", + "", + "```txt", + "this is a selection test", + "```", + "", + "[`diff-test.txt`](diff-test.txt)", + "", + "----", + "", + "", + "I can see the file `/Users/cam/tmp/a/diff-test.txt` contains \"this is a string\" on line 1.", + "", + "The two selection contexts you provided show:", + "1. Current content: \"this is a string\"", + "2. Expected/desired content: \"this is a selection test\"", + "", + "Since I'm in read-only mode, I can only observe that there's a difference between what's currently in the file and what one of the selections indicates should be there.", + "", + "" + ], + "timestamp": 1770748581 +} \ No newline at end of file diff --git a/tests/data/shifting-and-multiple-perms.expected.json b/tests/data/shifting-and-multiple-perms.expected.json index 6ab1fefc..611cad1c 100644 --- a/tests/data/shifting-and-multiple-perms.expected.json +++ b/tests/data/shifting-and-multiple-perms.expected.json @@ -6,21 +6,38 @@ 1, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-17 01:05:49)", "OpencodeHint"], - [" [msg_9efb39d68001J2h30a50B2774b]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-17 01:05:49)", + "OpencodeHint" + ], + [ + " [msg_9efb39d68001J2h30a50B2774b]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -28,14 +45,19 @@ 2, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -43,14 +65,19 @@ 3, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -58,14 +85,19 @@ 4, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -73,14 +105,19 @@ 5, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -88,21 +125,38 @@ 8, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-17 01:05:50)", "OpencodeHint"], - [" [msg_9efb39dc3002f81rMRqF2WO1UU]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-17 01:05:50)", + "OpencodeHint" + ], + [ + " [msg_9efb39dc3002f81rMRqF2WO1UU]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -110,21 +164,38 @@ 83, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-17 01:07:23)", "OpencodeHint"], - [" [msg_9efb50a0b001WFK7AMDV45cF8Z]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-17 01:07:23)", + "OpencodeHint" + ], + [ + " [msg_9efb50a0b001WFK7AMDV45cF8Z]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -132,14 +203,19 @@ 84, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -147,14 +223,19 @@ 85, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -162,21 +243,38 @@ 88, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-17 01:07:23)", "OpencodeHint"], - [" [msg_9efb50a2a002dzMgbQnasd86o1]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-17 01:07:23)", + "OpencodeHint" + ], + [ + " [msg_9efb50a2a002dzMgbQnasd86o1]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -184,21 +282,38 @@ 111, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-17 01:08:01)", "OpencodeHint"], - [" [msg_9efb59d93001LSm9y0DS9p8cP6]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-17 01:08:01)", + "OpencodeHint" + ], + [ + " [msg_9efb59d93001LSm9y0DS9p8cP6]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -206,14 +321,19 @@ 112, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -221,14 +341,19 @@ 113, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodeMessageRoleUser"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -3 } ], [ @@ -236,21 +361,38 @@ 116, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-17 01:08:01)", "OpencodeHint"], - [" [msg_9efb59db4002uWmyFRTjRIhIaQ]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-17 01:08:01)", + "OpencodeHint" + ], + [ + " [msg_9efb59db4002uWmyFRTjRIhIaQ]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 } ], [ @@ -258,37 +400,69 @@ 125, 0, { - "right_gravity": true, - "virt_text_win_col": -3, "ns_id": 3, - "virt_text_hide": false, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleSystem"], - [" "], - ["SYSTEM", "OpencodeMessageRoleSystem"], - ["", "OpencodeHint"], - ["", "OpencodeHint"], - [" [permission-display-message]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleSystem" + ], + [ + " " + ], + [ + "SYSTEM", + "OpencodeMessageRoleSystem" + ], + [ + "", + "OpencodeHint" + ], + [ + "", + "OpencodeHint" + ], + [ + " [permission-display-message]", + "OpencodeHint" + ] ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10 + "virt_text_win_col": -3 + } + ], + [ + 16, + 127, + 0, + { + "line_hl_group": "OpencodePermissionTitle", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [16, 127, 0, { "right_gravity": true, "ns_id": 3, "line_hl_group": "OpencodePermissionTitle", "priority": 4096 }], [ 17, 127, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -296,14 +470,19 @@ 128, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -311,14 +490,19 @@ 129, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -326,14 +510,19 @@ 130, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -341,14 +530,19 @@ 131, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -356,14 +550,19 @@ 132, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -371,14 +570,19 @@ 133, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -386,14 +590,19 @@ 134, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -401,14 +610,19 @@ 135, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -417,17 +631,22 @@ 0, { "end_col": 0, + "end_right_gravity": false, + "end_row": 137, + "hl_eol": true, "hl_group": "OpencodeDiffAdd", - "right_gravity": true, "ns_id": 3, + "priority": 5000, + "right_gravity": true, + "virt_text": [ + [ + "+", + "OpencodeDiffAdd" + ] + ], "virt_text_hide": false, - "end_row": 137, - "hl_eol": true, - "end_right_gravity": false, - "virt_text": [["+", "OpencodeDiffAdd"]], "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 5000 + "virt_text_repeat_linebreak": false } ], [ @@ -435,14 +654,19 @@ 136, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -450,14 +674,19 @@ 137, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -465,14 +694,19 @@ 138, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -480,14 +714,19 @@ 139, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -495,14 +734,19 @@ 140, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -510,14 +754,19 @@ 141, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -525,14 +774,19 @@ 142, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -540,30 +794,50 @@ 143, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 + } + ], + [ + 35, + 144, + 0, + { + "line_hl_group": "OpencodeDialogOptionHover", + "ns_id": 3, + "priority": 4096, + "right_gravity": true } ], - [35, 144, 0, { "right_gravity": true, "ns_id": 3, "line_hl_group": "OpencodeDialogOptionHover", "priority": 4096 }], [ 36, 144, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -571,13 +845,18 @@ 144, 2, { + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "› ", + "OpencodeDialogOptionHover" + ] + ], "virt_text_hide": false, - "ns_id": 3, - "virt_text": [["› ", "OpencodeDialogOptionHover"]], "virt_text_pos": "overlay", - "virt_text_repeat_linebreak": false, - "priority": 4096 + "virt_text_repeat_linebreak": false } ], [ @@ -585,14 +864,19 @@ 145, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -600,14 +884,19 @@ 146, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -615,14 +904,19 @@ 147, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ], [ @@ -630,18 +924,22 @@ 148, 0, { - "right_gravity": true, - "virt_text_win_col": -2, "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodePermissionBorder" + ] + ], "virt_text_hide": false, - "virt_text": [["▌", "OpencodePermissionBorder"]], "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096 + "virt_text_win_col": -2 } ] ], - "timestamp": 1769449853, "lines": [ "----", "", @@ -794,5 +1092,6 @@ "Navigate: `j`/`k` or `↑`/`↓` Select: `` or `1-3`", "", "" - ] -} + ], + "timestamp": 1770748581 +} \ No newline at end of file diff --git a/tests/data/simple-session.expected.json b/tests/data/simple-session.expected.json index ddec7561..bd107d06 100644 --- a/tests/data/simple-session.expected.json +++ b/tests/data/simple-session.expected.json @@ -1,27 +1,43 @@ { "actions": [], - "timestamp": 1766432128, "extmarks": [ [ 1, 1, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-10 19:18:25)", "OpencodeHint"], - [" [msg_9cf8f64de0016tbfTQqWMydbdr]", "OpencodeHint"] + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-10 19:18:25)", + "OpencodeHint" + ], + [ + " [msg_9cf8f64de0016tbfTQqWMydbdr]", + "OpencodeHint" + ] ], - "right_gravity": true, + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -29,14 +45,19 @@ 2, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -44,14 +65,19 @@ 3, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -59,14 +85,19 @@ 4, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -74,14 +105,19 @@ 5, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -89,14 +125,19 @@ 6, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -104,14 +145,19 @@ 7, 0, { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], + "ns_id": 3, + "priority": 4096, "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": true, - "priority": 4096, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ], [ @@ -119,21 +165,38 @@ 10, 0, { + "ns_id": 3, + "priority": 10, + "right_gravity": true, "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-10 19:18:25)", "OpencodeHint"], - [" [msg_9cf8f6549001tpoRuqkwS4Rxtl]", "OpencodeHint"] + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-10 19:18:25)", + "OpencodeHint" + ], + [ + " [msg_9cf8f6549001tpoRuqkwS4Rxtl]", + "OpencodeHint" + ] ], - "right_gravity": true, + "virt_text_hide": false, "virt_text_pos": "win_col", "virt_text_repeat_linebreak": false, - "priority": 10, - "virt_text_hide": false, - "virt_text_win_col": -3, - "ns_id": 3 + "virt_text_win_col": -3 } ] ], @@ -153,5 +216,6 @@ "1", "", "" - ] -} + ], + "timestamp": 1770748581 +} \ No newline at end of file diff --git a/tests/data/tool-invalid.expected.json b/tests/data/tool-invalid.expected.json index 66e8fff5..222f8caf 100644 --- a/tests/data/tool-invalid.expected.json +++ b/tests/data/tool-invalid.expected.json @@ -1,40 +1,43 @@ { - "timestamp": 1766432128, "actions": [], - "lines": [ - "----", - "", - "", - "** tool** `invalid`", - "", - "> [!ERROR]", - ">", - "> Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.", - "> Error message: JSON Parse error: Unterminated string", - "", - "" - ], "extmarks": [ [ 1, 1, 0, { - "virt_text_repeat_linebreak": false, - "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["BUILD", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4.5", "OpencodeHint"], - [" (2025-10-13 20:10:06)", "OpencodeHint"], - [" [msg_9df31cc90001HGn2UbFUgqJnLr]", "OpencodeHint"] - ], "ns_id": 3, - "virt_text_hide": false, "priority": 10, "right_gravity": true, - "virt_text_win_col": -3, - "virt_text_pos": "win_col" + "virt_text": [ + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "BUILD", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4.5", + "OpencodeHint" + ], + [ + " (2025-10-13 20:10:06)", + "OpencodeHint" + ], + [ + " [msg_9df31cc90001HGn2UbFUgqJnLr]", + "OpencodeHint" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 } ], [ @@ -42,14 +45,19 @@ 3, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -57,14 +65,19 @@ 4, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -72,14 +85,19 @@ 5, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -87,14 +105,19 @@ 6, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -102,14 +125,19 @@ 7, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ], [ @@ -117,15 +145,34 @@ 8, 0, { - "virt_text_repeat_linebreak": true, - "virt_text": [["▌", "OpencodeToolBorder"]], "ns_id": 3, - "virt_text_hide": false, "priority": 4096, "right_gravity": true, - "virt_text_win_col": -1, - "virt_text_pos": "win_col" + "virt_text": [ + [ + "▌", + "OpencodeToolBorder" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -1 } ] - ] -} + ], + "lines": [ + "----", + "", + "", + "** tool** `invalid`", + "", + "> [!ERROR]", + ">", + "> Invalid input for tool edit: JSON parsing failed: Text: {\"filePath\": \"/Users/cam/Dev/neovim-dev/opencode.nvim/lua/opencode/ui/streaming_renderer.lua\", \"newString\": \"---Event handler for permission.replied events\\n---Re-renders part after permission is resolved\\n---@param event table Event object\\nfunctio.", + "> Error message: JSON Parse error: Unterminated string", + "", + "" + ], + "timestamp": 1770748582 +} \ No newline at end of file diff --git a/tests/data/updating-text.expected.json b/tests/data/updating-text.expected.json index 9941c802..bbff6aa0 100644 --- a/tests/data/updating-text.expected.json +++ b/tests/data/updating-text.expected.json @@ -1,6 +1,165 @@ { - "timestamp": 1769091027, "actions": [], + "extmarks": [ + [ + 1, + 1, + 0, + { + "ns_id": 3, + "priority": 10, + "right_gravity": true, + "virt_text": [ + [ + "▌󰭻 ", + "OpencodeMessageRoleUser" + ], + [ + " " + ], + [ + "USER", + "OpencodeMessageRoleUser" + ], + [ + "", + "OpencodeHint" + ], + [ + " (2025-10-10 22:06:43)", + "OpencodeHint" + ], + [ + " [msg_9d0297a630014CA5ly3Vvw8Kt5]", + "OpencodeHint" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ], + [ + 2, + 2, + 0, + { + "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 + } + ], + [ + 3, + 3, + 0, + { + "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 + } + ], + [ + 4, + 4, + 0, + { + "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 + } + ], + [ + 5, + 5, + 0, + { + "ns_id": 3, + "priority": 4096, + "right_gravity": true, + "virt_text": [ + [ + "▌", + "OpencodeMessageRoleUser" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": true, + "virt_text_win_col": -3 + } + ], + [ + 6, + 8, + 0, + { + "ns_id": 3, + "priority": 10, + "right_gravity": true, + "virt_text": [ + [ + " ", + "OpencodeMessageRoleAssistant" + ], + [ + " " + ], + [ + "PLAN", + "OpencodeMessageRoleAssistant" + ], + [ + " claude-sonnet-4", + "OpencodeHint" + ], + [ + " (2025-10-10 22:06:43)", + "OpencodeHint" + ], + [ + " [msg_9d0297ab3001UGZU9fDJM4Y75w]", + "OpencodeHint" + ] + ], + "virt_text_hide": false, + "virt_text_pos": "win_col", + "virt_text_repeat_linebreak": false, + "virt_text_win_col": -3 + } + ] + ], "lines": [ "----", "", @@ -65,110 +224,5 @@ "", "" ], - "extmarks": [ - [ - 1, - 1, - 0, - { - "virt_text": [ - ["▌󰭻 ", "OpencodeMessageRoleUser"], - [" "], - ["USER", "OpencodeMessageRoleUser"], - ["", "OpencodeHint"], - [" (2025-10-10 22:06:43)", "OpencodeHint"], - [" [msg_9d0297a630014CA5ly3Vvw8Kt5]", "OpencodeHint"] - ], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false, - "virt_text_hide": false - } - ], - [ - 2, - 2, - 0, - { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true, - "virt_text_hide": false - } - ], - [ - 3, - 3, - 0, - { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true, - "virt_text_hide": false - } - ], - [ - 4, - 4, - 0, - { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true, - "virt_text_hide": false - } - ], - [ - 5, - 5, - 0, - { - "virt_text": [["▌", "OpencodeMessageRoleUser"]], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 4096, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": true, - "virt_text_hide": false - } - ], - [ - 6, - 8, - 0, - { - "virt_text": [ - [" ", "OpencodeMessageRoleAssistant"], - [" "], - ["PLAN", "OpencodeMessageRoleAssistant"], - [" claude-sonnet-4", "OpencodeHint"], - [" (2025-10-10 22:06:43)", "OpencodeHint"], - [" [msg_9d0297ab3001UGZU9fDJM4Y75w]", "OpencodeHint"] - ], - "ns_id": 3, - "virt_text_pos": "win_col", - "right_gravity": true, - "priority": 10, - "virt_text_win_col": -3, - "virt_text_repeat_linebreak": false, - "virt_text_hide": false - } - ] - ] -} + "timestamp": 1770748582 +} \ No newline at end of file